diff options
-rw-r--r-- | libc/Android.mk | 2 | ||||
-rw-r--r-- | libc/bionic/dirent.cpp | 6 | ||||
-rw-r--r-- | libc/bionic/pthread.c | 231 | ||||
-rw-r--r-- | libc/bionic/pthread_attr.cpp | 165 | ||||
-rw-r--r-- | libc/bionic/pthread_internal.h | 4 | ||||
-rw-r--r-- | libc/bionic/pthread_key.cpp | 25 | ||||
-rw-r--r-- | libc/bionic/pthread_setname_np.cpp | 79 | ||||
-rw-r--r-- | libc/bionic/pthread_sigmask.cpp | 13 | ||||
-rw-r--r-- | libc/bionic/strerror_r.cpp | 7 | ||||
-rw-r--r-- | libc/bionic/stubs.cpp | 19 | ||||
-rw-r--r-- | libc/bionic/sysconf.cpp | 2 | ||||
-rw-r--r-- | libc/bionic/tmpfile.cpp | 8 | ||||
-rw-r--r-- | libc/private/ErrnoRestorer.h | 43 | ||||
-rw-r--r-- | libc/private/ThreadLocalBuffer.h | 7 | ||||
-rw-r--r-- | libc/private/bionic_futex.h | 4 | ||||
-rw-r--r-- | libc/private/bionic_tls.h | 28 | ||||
-rw-r--r-- | tests/Android.mk | 1 | ||||
-rw-r--r-- | tests/pthread_test.cpp | 40 |
18 files changed, 391 insertions, 293 deletions
diff --git a/libc/Android.mk b/libc/Android.mk index 0f61bcf..bf4ca7b 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -279,6 +279,8 @@ libc_bionic_src_files := \ bionic/__memcpy_chk.cpp \ bionic/__memmove_chk.cpp \ bionic/__memset_chk.cpp \ + bionic/pthread_attr.cpp \ + bionic/pthread_setname_np.cpp \ bionic/pthread_sigmask.cpp \ bionic/raise.cpp \ bionic/sbrk.cpp \ diff --git a/libc/bionic/dirent.cpp b/libc/bionic/dirent.cpp index 3a7b5b4..74297d8 100644 --- a/libc/bionic/dirent.cpp +++ b/libc/bionic/dirent.cpp @@ -36,7 +36,8 @@ #include <sys/types.h> #include <unistd.h> -#include <private/ScopedPthreadMutexLocker.h> +#include "private/ErrnoRestorer.h" +#include "private/ScopedPthreadMutexLocker.h" struct DIR { int fd_; @@ -108,7 +109,7 @@ dirent* readdir(DIR* d) { } int readdir_r(DIR* d, dirent* entry, dirent** result) { - int saved_errno = errno; + ErrnoRestorer errno_restorer; *result = NULL; errno = 0; @@ -124,7 +125,6 @@ int readdir_r(DIR* d, dirent* entry, dirent** result) { memcpy(entry, next, next->d_reclen); *result = entry; } - errno = saved_errno; return 0; } diff --git a/libc/bionic/pthread.c b/libc/bionic/pthread.c index e1ace7d..bdf2b87 100644 --- a/libc/bionic/pthread.c +++ b/libc/bionic/pthread.c @@ -26,23 +26,12 @@ * SUCH DAMAGE. */ -#include <assert.h> +#include <pthread.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" @@ -84,23 +73,8 @@ void ATTRIBUTES _thread_created_hook(pid_t thread_id); static const int kPthreadInitFailed = 1; -#define PTHREAD_ATTR_FLAG_DETACHED 0x00000001 -#define PTHREAD_ATTR_FLAG_USER_STACK 0x00000002 - -#define DEFAULT_STACKSIZE (1024 * 1024) - static pthread_mutex_t mmap_lock = PTHREAD_MUTEX_INITIALIZER; - -static const pthread_attr_t gDefaultPthreadAttr = { - .flags = 0, - .stack_base = NULL, - .stack_size = DEFAULT_STACKSIZE, - .guard_size = PAGE_SIZE, - .sched_policy = SCHED_NORMAL, - .sched_priority = 0 -}; - __LIBC_HIDDEN__ pthread_internal_t* gThreadList = NULL; __LIBC_HIDDEN__ pthread_mutex_t gThreadListLock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t gDebuggerNotificationLock = PTHREAD_MUTEX_INITIALIZER; @@ -306,7 +280,7 @@ int pthread_create(pthread_t *thread_out, pthread_attr_t const * attr, pthread_internal_t* thread = calloc(sizeof(*thread), 1); if (thread == NULL) { - return ENOMEM; + return EAGAIN; } thread->allocated_on_heap = true; @@ -321,7 +295,7 @@ int pthread_create(pthread_t *thread_out, pthread_attr_t const * attr, stack = mkstack(stack_size, attr->guard_size); if (stack == NULL) { free(thread); - return ENOMEM; + return EAGAIN; } } @@ -379,152 +353,6 @@ int pthread_create(pthread_t *thread_out, pthread_attr_t const * attr, } -int pthread_attr_init(pthread_attr_t * attr) -{ - *attr = gDefaultPthreadAttr; - return 0; -} - -int pthread_attr_destroy(pthread_attr_t * attr) -{ - memset(attr, 0x42, sizeof(pthread_attr_t)); - return 0; -} - -int pthread_attr_setdetachstate(pthread_attr_t * attr, int state) -{ - if (state == PTHREAD_CREATE_DETACHED) { - attr->flags |= PTHREAD_ATTR_FLAG_DETACHED; - } else if (state == PTHREAD_CREATE_JOINABLE) { - attr->flags &= ~PTHREAD_ATTR_FLAG_DETACHED; - } else { - return EINVAL; - } - return 0; -} - -int pthread_attr_getdetachstate(pthread_attr_t const * attr, int * state) -{ - *state = (attr->flags & PTHREAD_ATTR_FLAG_DETACHED) - ? PTHREAD_CREATE_DETACHED - : PTHREAD_CREATE_JOINABLE; - return 0; -} - -int pthread_attr_setschedpolicy(pthread_attr_t * attr, int policy) -{ - attr->sched_policy = policy; - return 0; -} - -int pthread_attr_getschedpolicy(pthread_attr_t const * attr, int * policy) -{ - *policy = attr->sched_policy; - return 0; -} - -int pthread_attr_setschedparam(pthread_attr_t * attr, struct sched_param const * param) -{ - attr->sched_priority = param->sched_priority; - return 0; -} - -int pthread_attr_getschedparam(pthread_attr_t const * attr, struct sched_param * param) -{ - param->sched_priority = attr->sched_priority; - return 0; -} - -int pthread_attr_setstacksize(pthread_attr_t * attr, size_t stack_size) -{ - if ((stack_size & (PAGE_SIZE - 1) || stack_size < PTHREAD_STACK_MIN)) { - return EINVAL; - } - attr->stack_size = stack_size; - return 0; -} - -int pthread_attr_getstacksize(pthread_attr_t const * attr, size_t * stack_size) -{ - *stack_size = attr->stack_size; - return 0; -} - -int pthread_attr_setstackaddr(pthread_attr_t * attr __attribute__((unused)), - void * stack_addr __attribute__((unused))) -{ - // This was removed from POSIX.1-2008, and is not implemented on bionic. - // Needed for ABI compatibility with the NDK. - return ENOSYS; -} - -int pthread_attr_getstackaddr(pthread_attr_t const * attr, void ** stack_addr) -{ - // This was removed from POSIX.1-2008. - // Needed for ABI compatibility with the NDK. - *stack_addr = (char*)attr->stack_base + attr->stack_size; - return 0; -} - -int pthread_attr_setstack(pthread_attr_t * attr, void * stack_base, size_t stack_size) -{ - if ((stack_size & (PAGE_SIZE - 1) || stack_size < PTHREAD_STACK_MIN)) { - return EINVAL; - } - if ((uint32_t)stack_base & (PAGE_SIZE - 1)) { - return EINVAL; - } - attr->stack_base = stack_base; - attr->stack_size = stack_size; - return 0; -} - -int pthread_attr_getstack(pthread_attr_t const * attr, void ** stack_base, size_t * stack_size) -{ - *stack_base = attr->stack_base; - *stack_size = attr->stack_size; - return 0; -} - -int pthread_attr_setguardsize(pthread_attr_t * attr, size_t guard_size) -{ - if (guard_size & (PAGE_SIZE - 1) || guard_size < PAGE_SIZE) { - return EINVAL; - } - - attr->guard_size = guard_size; - return 0; -} - -int pthread_attr_getguardsize(pthread_attr_t const * attr, size_t * guard_size) -{ - *guard_size = attr->guard_size; - return 0; -} - -int pthread_getattr_np(pthread_t thid, pthread_attr_t * attr) -{ - pthread_internal_t * thread = (pthread_internal_t *)thid; - *attr = thread->attr; - return 0; -} - -int pthread_attr_setscope(pthread_attr_t *attr __attribute__((unused)), int scope) -{ - if (scope == PTHREAD_SCOPE_SYSTEM) - return 0; - if (scope == PTHREAD_SCOPE_PROCESS) - return ENOTSUP; - - return EINVAL; -} - -int pthread_attr_getscope(pthread_attr_t const *attr __attribute__((unused))) -{ - return PTHREAD_SCOPE_SYSTEM; -} - - /* CAVEAT: our implementation of pthread_cleanup_push/pop doesn't support C++ exceptions * and thread cancelation */ @@ -1881,57 +1709,6 @@ int pthread_once( pthread_once_t* once_control, void (*init_routine)(void) ) return 0; } -/* This value is not exported by kernel headers, so hardcode it here */ -#define MAX_TASK_COMM_LEN 16 -#define TASK_COMM_FMT "/proc/self/task/%u/comm" - -int pthread_setname_np(pthread_t thid, const char *thname) -{ - size_t thname_len; - int saved_errno, ret; - - if (thid == 0 || thname == NULL) - return EINVAL; - - thname_len = strlen(thname); - if (thname_len >= MAX_TASK_COMM_LEN) - return ERANGE; - - saved_errno = errno; - if (thid == pthread_self()) - { - ret = prctl(PR_SET_NAME, (unsigned long)thname, 0, 0, 0) ? errno : 0; - } - else - { - /* Have to change another thread's name */ - pthread_internal_t *thread = (pthread_internal_t *)thid; - char comm_name[sizeof(TASK_COMM_FMT) + 8]; - ssize_t n; - int fd; - - snprintf(comm_name, sizeof(comm_name), TASK_COMM_FMT, (unsigned int)thread->kernel_id); - fd = open(comm_name, O_RDWR); - if (fd == -1) - { - ret = errno; - goto exit; - } - n = TEMP_FAILURE_RETRY(write(fd, thname, thname_len)); - close(fd); - - if (n < 0) - ret = errno; - else if ((size_t)n != thname_len) - ret = EIO; - else - ret = 0; - } -exit: - errno = saved_errno; - return ret; -} - /* Return the kernel thread ID for a pthread. * This is only defined for implementations where pthread <-> kernel is 1:1, which this is. * Not the same as pthread_getthreadid_np, which is commonly defined to be opaque. diff --git a/libc/bionic/pthread_attr.cpp b/libc/bionic/pthread_attr.cpp new file mode 100644 index 0000000..831a28e --- /dev/null +++ b/libc/bionic/pthread_attr.cpp @@ -0,0 +1,165 @@ +/* + * 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 "pthread_internal.h" + +#define DEFAULT_STACKSIZE (1024 * 1024) + +const pthread_attr_t gDefaultPthreadAttr = { + .flags = 0, + .stack_base = NULL, + .stack_size = DEFAULT_STACKSIZE, + .guard_size = PAGE_SIZE, + .sched_policy = SCHED_NORMAL, + .sched_priority = 0 +}; + +int pthread_attr_init(pthread_attr_t* attr) { + *attr = gDefaultPthreadAttr; + return 0; +} + +int pthread_attr_destroy(pthread_attr_t* attr) { + memset(attr, 0x42, sizeof(pthread_attr_t)); + return 0; +} + +int pthread_attr_setdetachstate(pthread_attr_t* attr, int state) { + if (state == PTHREAD_CREATE_DETACHED) { + attr->flags |= PTHREAD_ATTR_FLAG_DETACHED; + } else if (state == PTHREAD_CREATE_JOINABLE) { + attr->flags &= ~PTHREAD_ATTR_FLAG_DETACHED; + } else { + return EINVAL; + } + return 0; +} + +int pthread_attr_getdetachstate(pthread_attr_t const* attr, int* state) { + *state = (attr->flags & PTHREAD_ATTR_FLAG_DETACHED) ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE; + return 0; +} + +int pthread_attr_setschedpolicy(pthread_attr_t* attr, int policy) { + attr->sched_policy = policy; + return 0; +} + +int pthread_attr_getschedpolicy(pthread_attr_t const* attr, int* policy) { + *policy = attr->sched_policy; + return 0; +} + +int pthread_attr_setschedparam(pthread_attr_t * attr, struct sched_param const* param) { + attr->sched_priority = param->sched_priority; + return 0; +} + +int pthread_attr_getschedparam(pthread_attr_t const* attr, struct sched_param* param) { + param->sched_priority = attr->sched_priority; + return 0; +} + +int pthread_attr_setstacksize(pthread_attr_t* attr, size_t stack_size) { + if ((stack_size & (PAGE_SIZE - 1) || stack_size < PTHREAD_STACK_MIN)) { + return EINVAL; + } + attr->stack_size = stack_size; + return 0; +} + +int pthread_attr_getstacksize(pthread_attr_t const* attr, size_t* stack_size) { + *stack_size = attr->stack_size; + return 0; +} + +int pthread_attr_setstackaddr(pthread_attr_t*, void*) { + // This was removed from POSIX.1-2008, and is not implemented on bionic. + // Needed for ABI compatibility with the NDK. + return ENOSYS; +} + +int pthread_attr_getstackaddr(pthread_attr_t const* attr, void** stack_addr) { + // This was removed from POSIX.1-2008. + // Needed for ABI compatibility with the NDK. + *stack_addr = (char*)attr->stack_base + attr->stack_size; + return 0; +} + +int pthread_attr_setstack(pthread_attr_t* attr, void* stack_base, size_t stack_size) { + if ((stack_size & (PAGE_SIZE - 1) || stack_size < PTHREAD_STACK_MIN)) { + return EINVAL; + } + if ((uint32_t)stack_base & (PAGE_SIZE - 1)) { + return EINVAL; + } + attr->stack_base = stack_base; + attr->stack_size = stack_size; + return 0; +} + +int pthread_attr_getstack(pthread_attr_t const* attr, void** stack_base, size_t* stack_size) { + *stack_base = attr->stack_base; + *stack_size = attr->stack_size; + return 0; +} + +int pthread_attr_setguardsize(pthread_attr_t* attr, size_t guard_size) { + if (guard_size & (PAGE_SIZE - 1) || guard_size < PAGE_SIZE) { + return EINVAL; + } + attr->guard_size = guard_size; + return 0; +} + +int pthread_attr_getguardsize(pthread_attr_t const* attr, size_t* guard_size) { + *guard_size = attr->guard_size; + return 0; +} + +int pthread_getattr_np(pthread_t thid, pthread_attr_t* attr) { + pthread_internal_t* thread = (pthread_internal_t*) thid; + *attr = thread->attr; + return 0; +} + +int pthread_attr_setscope(pthread_attr_t* , int scope) { + if (scope == PTHREAD_SCOPE_SYSTEM) { + return 0; + } + if (scope == PTHREAD_SCOPE_PROCESS) { + return ENOTSUP; + } + return EINVAL; +} + +int pthread_attr_getscope(pthread_attr_t const*) { + return PTHREAD_SCOPE_SYSTEM; +} diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h index 24b420c..63071d9 100644 --- a/libc/bionic/pthread_internal.h +++ b/libc/bionic/pthread_internal.h @@ -62,6 +62,10 @@ pthread_internal_t* __get_thread(void); __LIBC_HIDDEN__ void pthread_key_clean_all(void); +#define PTHREAD_ATTR_FLAG_DETACHED 0x00000001 +#define PTHREAD_ATTR_FLAG_USER_STACK 0x00000002 + +extern __LIBC_HIDDEN__ const pthread_attr_t gDefaultPthreadAttr; extern pthread_internal_t* gThreadList; extern pthread_mutex_t gThreadListLock; diff --git a/libc/bionic/pthread_key.cpp b/libc/bionic/pthread_key.cpp index 00dacba..b01f9bd 100644 --- a/libc/bionic/pthread_key.cpp +++ b/libc/bionic/pthread_key.cpp @@ -26,33 +26,10 @@ * 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: * diff --git a/libc/bionic/pthread_setname_np.cpp b/libc/bionic/pthread_setname_np.cpp new file mode 100644 index 0000000..88b86ec --- /dev/null +++ b/libc/bionic/pthread_setname_np.cpp @@ -0,0 +1,79 @@ +/* + * 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 <fcntl.h> +#include <stdio.h> // For snprintf. +#include <sys/prctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "pthread_internal.h" +#include "private/ErrnoRestorer.h" + +// This value is not exported by kernel headers. +#define MAX_TASK_COMM_LEN 16 +#define TASK_COMM_FMT "/proc/self/task/%u/comm" + +int pthread_setname_np(pthread_t thread, const char* thread_name) { + ErrnoRestorer errno_restorer; + + if (thread == 0 || thread_name == NULL) { + return EINVAL; + } + + size_t thread_name_len = strlen(thread_name); + if (thread_name_len >= MAX_TASK_COMM_LEN) { + return ERANGE; + } + + // Changing our own name is an easy special case. + if (thread == pthread_self()) { + return prctl(PR_SET_NAME, (unsigned long)thread_name, 0, 0, 0) ? errno : 0; + } + + // Have to change another thread's name. + pthread_internal_t* t = reinterpret_cast<pthread_internal_t*>(thread); + char comm_name[sizeof(TASK_COMM_FMT) + 8]; + snprintf(comm_name, sizeof(comm_name), TASK_COMM_FMT, (unsigned int) t->kernel_id); + int fd = open(comm_name, O_RDWR); + if (fd == -1) { + return errno; + } + ssize_t n = TEMP_FAILURE_RETRY(write(fd, thread_name, thread_name_len)); + close(fd); + + if (n < 0) { + return errno; + } else if ((size_t)n != thread_name_len) { + return EIO; + } + return 0; +} diff --git a/libc/bionic/pthread_sigmask.cpp b/libc/bionic/pthread_sigmask.cpp index 9c9dc3e..e4e1b2b 100644 --- a/libc/bionic/pthread_sigmask.cpp +++ b/libc/bionic/pthread_sigmask.cpp @@ -30,12 +30,13 @@ #include <pthread.h> #include <signal.h> -#include <private/kernel_sigset_t.h> +#include "private/ErrnoRestorer.h" +#include "private/kernel_sigset_t.h" extern "C" int __rt_sigprocmask(int, const kernel_sigset_t*, kernel_sigset_t*, size_t); int pthread_sigmask(int how, const sigset_t* iset, sigset_t* oset) { - int old_errno = errno; + ErrnoRestorer errno_restorer; // 'in_set_ptr' is the second parameter to __rt_sigprocmask. It must be NULL // if 'set' is NULL to ensure correct semantics (which in this case would @@ -48,15 +49,13 @@ int pthread_sigmask(int how, const sigset_t* iset, sigset_t* oset) { } kernel_sigset_t out_set; - int result = __rt_sigprocmask(how, in_set_ptr, &out_set, sizeof(out_set)); - if (result < 0) { - result = errno; + if (__rt_sigprocmask(how, in_set_ptr, &out_set, sizeof(out_set)) == -1) { + return errno; } if (oset != NULL) { *oset = out_set.bionic; } - errno = old_errno; - return result; + return 0; } diff --git a/libc/bionic/strerror_r.cpp b/libc/bionic/strerror_r.cpp index 646cc52..81120ec 100644 --- a/libc/bionic/strerror_r.cpp +++ b/libc/bionic/strerror_r.cpp @@ -7,6 +7,8 @@ #include <stdio.h> #include <string.h> +#include "private/ErrnoRestorer.h" + struct Pair { int code; const char* msg; @@ -42,7 +44,7 @@ extern "C" __LIBC_HIDDEN__ const char* __strsignal_lookup(int signal_number) { } int strerror_r(int error_number, char* buf, size_t buf_len) { - int saved_errno = errno; + ErrnoRestorer errno_restorer; size_t length; const char* error_name = __strerror_lookup(error_number); @@ -52,11 +54,10 @@ int strerror_r(int error_number, char* buf, size_t buf_len) { length = snprintf(buf, buf_len, "Unknown error %d", error_number); } if (length >= buf_len) { - errno = ERANGE; + errno_restorer.override(ERANGE); return -1; } - errno = saved_errno; return 0; } diff --git a/libc/bionic/stubs.cpp b/libc/bionic/stubs.cpp index 3f24d1b..8ddc326 100644 --- a/libc/bionic/stubs.cpp +++ b/libc/bionic/stubs.cpp @@ -31,15 +31,17 @@ #include <grp.h> #include <mntent.h> #include <netdb.h> -#include <private/android_filesystem_config.h> -#include <private/debug_format.h> -#include <private/logd.h> #include <pthread.h> #include <pwd.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> +#include "private/android_filesystem_config.h" +#include "private/debug_format.h" +#include "private/ErrnoRestorer.h" +#include "private/logd.h" + // Thread-specific state for the non-reentrant functions. static pthread_once_t stubs_once = PTHREAD_ONCE_INIT; static pthread_key_t stubs_key; @@ -58,7 +60,7 @@ static int do_getpw_r(int by_name, const char* name, uid_t uid, passwd** result) { // getpwnam_r and getpwuid_r don't modify errno, but library calls we // make might. - int old_errno = errno; + ErrnoRestorer errno_restorer; *result = NULL; // Our implementation of getpwnam(3) and getpwuid(3) use thread-local @@ -69,9 +71,7 @@ static int do_getpw_r(int by_name, const char* name, uid_t uid, // POSIX allows failure to find a match to be considered a non-error. // Reporting success (0) but with *result NULL is glibc's behavior. if (src == NULL) { - int rc = (errno == ENOENT) ? 0 : errno; - errno = old_errno; - return rc; + return (errno == ENOENT) ? 0 : errno; } // Work out where our strings will go in 'buf', and whether we've got @@ -84,13 +84,11 @@ static int do_getpw_r(int by_name, const char* name, uid_t uid, dst->pw_shell = buf + required_byte_count; required_byte_count += strlen(src->pw_shell) + 1; if (byte_count < required_byte_count) { - errno = old_errno; return ERANGE; } // Copy the strings. - snprintf(buf, byte_count, "%s%c%s%c%s", - src->pw_name, 0, src->pw_dir, 0, src->pw_shell); + snprintf(buf, byte_count, "%s%c%s%c%s", src->pw_name, 0, src->pw_dir, 0, src->pw_shell); // pw_passwd is non-POSIX and unused (always NULL) in bionic. // pw_gecos is non-POSIX and missing in bionic. @@ -101,7 +99,6 @@ static int do_getpw_r(int by_name, const char* name, uid_t uid, dst->pw_uid = src->pw_uid; *result = dst; - errno = old_errno; return 0; } diff --git a/libc/bionic/sysconf.cpp b/libc/bionic/sysconf.cpp index f4845e1..869faef 100644 --- a/libc/bionic/sysconf.cpp +++ b/libc/bionic/sysconf.cpp @@ -306,7 +306,7 @@ int sysconf(int name) { return _POSIX_THREAD_DESTRUCTOR_ITERATIONS; case _SC_THREAD_KEYS_MAX: - return (BIONIC_TLS_SLOTS - TLS_SLOT_FIRST_USER_SLOT); + return (BIONIC_TLS_SLOTS - TLS_SLOT_FIRST_USER_SLOT - GLOBAL_INIT_THREAD_LOCAL_BUFFER_COUNT); case _SC_THREAD_STACK_MIN: return SYSTEM_THREAD_STACK_MIN; case _SC_THREAD_THREADS_MAX: return SYSTEM_THREAD_THREADS_MAX; diff --git a/libc/bionic/tmpfile.cpp b/libc/bionic/tmpfile.cpp index b97cc6c..8419ff5 100644 --- a/libc/bionic/tmpfile.cpp +++ b/libc/bionic/tmpfile.cpp @@ -38,6 +38,8 @@ #include <sys/types.h> #include <unistd.h> +#include "private/ErrnoRestorer.h" + class ScopedSignalBlocker { public: ScopedSignalBlocker() { @@ -77,9 +79,8 @@ static FILE* __tmpfile_dir(const char* tmp_dir) { struct stat sb; int rc = fstat(fd, &sb); if (rc == -1) { - int old_errno = errno; + ErrnoRestorer errno_restorer; close(fd); - errno = old_errno; return NULL; } } @@ -91,9 +92,8 @@ static FILE* __tmpfile_dir(const char* tmp_dir) { } // Failure. Clean up. We already unlinked, so we just need to close. - int old_errno = errno; + ErrnoRestorer errno_restorer; close(fd); - errno = old_errno; return NULL; } diff --git a/libc/private/ErrnoRestorer.h b/libc/private/ErrnoRestorer.h new file mode 100644 index 0000000..ed6ab62 --- /dev/null +++ b/libc/private/ErrnoRestorer.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ERRNO_RESTORER_H +#define ERRNO_RESTORER_H + +#include <errno.h> + +class ErrnoRestorer { + public: + explicit ErrnoRestorer() : saved_errno_(errno) { + } + + ~ErrnoRestorer() { + errno = saved_errno_; + } + + void override(int new_errno) { + saved_errno_ = new_errno; + } + + private: + int saved_errno_; + + // Disallow copy and assignment. + ErrnoRestorer(const ErrnoRestorer&); + void operator=(const ErrnoRestorer&); +}; + +#endif // ERRNO_RESTORER_H diff --git a/libc/private/ThreadLocalBuffer.h b/libc/private/ThreadLocalBuffer.h index 1c5e3f4..e5bd28c 100644 --- a/libc/private/ThreadLocalBuffer.h +++ b/libc/private/ThreadLocalBuffer.h @@ -36,19 +36,20 @@ // so we make do with macros instead of a C++ class. // TODO: move __cxa_guard_acquire and __cxa_guard_release into libc. +// We used to use pthread_once to initialize the keys, but life is more predictable +// if we allocate them all up front when the C library starts up, via __constructor__. + #define GLOBAL_INIT_THREAD_LOCAL_BUFFER(name) \ - static pthread_once_t __bionic_tls_ ## name ## _once; \ static pthread_key_t __bionic_tls_ ## name ## _key; \ static void __bionic_tls_ ## name ## _key_destroy(void* buffer) { \ free(buffer); \ } \ - static void __bionic_tls_ ## name ## _key_init() { \ + __attribute__((constructor)) static void __bionic_tls_ ## name ## _key_init() { \ pthread_key_create(&__bionic_tls_ ## name ## _key, __bionic_tls_ ## name ## _key_destroy); \ } // Leaves "name_tls_buffer" and "name_tls_buffer_size" defined and initialized. #define LOCAL_INIT_THREAD_LOCAL_BUFFER(type, name, byte_count) \ - pthread_once(&__bionic_tls_ ## name ## _once, __bionic_tls_ ## name ## _key_init); \ type name ## _tls_buffer = \ reinterpret_cast<type>(pthread_getspecific(__bionic_tls_ ## name ## _key)); \ if (name ## _tls_buffer == NULL) { \ diff --git a/libc/private/bionic_futex.h b/libc/private/bionic_futex.h index eb49fb7..6c7fdbe 100644 --- a/libc/private/bionic_futex.h +++ b/libc/private/bionic_futex.h @@ -30,6 +30,8 @@ #include <linux/futex.h> +__BEGIN_DECLS + extern int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout); extern int __futex_wake(volatile void *ftx, int count); @@ -54,4 +56,6 @@ extern int __futex_syscall4(volatile void *ftx, int op, int val, const struct ti extern int __futex_wake_ex(volatile void *ftx, int pshared, int val); extern int __futex_wait_ex(volatile void *ftx, int pshared, int val, const struct timespec *timeout); +__END_DECLS + #endif /* _BIONIC_FUTEX_H */ diff --git a/libc/private/bionic_tls.h b/libc/private/bionic_tls.h index edf878f..4dd66c0 100644 --- a/libc/private/bionic_tls.h +++ b/libc/private/bionic_tls.h @@ -43,28 +43,40 @@ __BEGIN_DECLS ** pre-allocated slot directly for performance reason). **/ -/* Maximum number of elements in the TLS array. */ -#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, + + /* These two aren't used by bionic itself, but allow the graphics code to + * access TLS directly rather than using the pthread API. */ TLS_SLOT_OPENGL_API = 3, TLS_SLOT_OPENGL = 4, + + /* 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. */ + TLS_SLOT_BIONIC_PREINIT = TLS_SLOT_OPENGL_API, + TLS_SLOT_STACK_GUARD = 5, /* GCC requires this specific slot for x86. */ 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 that isn't needed during libc startup. +/* + * Maximum number of elements in the TLS array. + * POSIX says this must be at least 128, but Android has traditionally had only 64, minus those + * ones used internally by bionic itself. + * There are two kinds of slot used internally by bionic --- there are the well-known slots + * enumerated above, and then there are those that are allocated during startup by calls to + * pthread_key_create; grep for GLOBAL_INIT_THREAD_LOCAL_BUFFER to find those. We need to manually + * maintain that second number, but pthread_test will fail if we forget. */ -#define TLS_SLOT_BIONIC_PREINIT TLS_SLOT_OPENGL_API +#define GLOBAL_INIT_THREAD_LOCAL_BUFFER_COUNT 4 +#define BIONIC_TLS_SLOTS 64 /* 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/tests/Android.mk b/tests/Android.mk index 3217a4d..8138cff 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -124,6 +124,7 @@ endif # implementation for testing the tests themselves. ifeq ($(HOST_OS)-$(HOST_ARCH),linux-x86) include $(CLEAR_VARS) +LOCAL_CXX := /usr/bin/g++ # Avoid the host prebuilt so we test the real glibc. LOCAL_MODULE := bionic-unit-tests-glibc LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_CFLAGS += $(test_c_flags) diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp index 0ccd948..00aa4b1 100644 --- a/tests/pthread_test.cpp +++ b/tests/pthread_test.cpp @@ -28,12 +28,14 @@ TEST(pthread, pthread_key_create) { ASSERT_EQ(EINVAL, pthread_key_delete(key)); } +#if !defined(__GLIBC__) // glibc uses keys internally that its sysconf value doesn't account for. 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)); + // If this fails, it's likely that GLOBAL_INIT_THREAD_LOCAL_BUFFER_COUNT is wrong. + ASSERT_EQ(0, pthread_key_create(&key, NULL)) << i << " of " << sysconf(_SC_THREAD_KEYS_MAX); keys.push_back(key); } @@ -46,6 +48,7 @@ TEST(pthread, pthread_key_create_lots) { ASSERT_EQ(0, pthread_key_delete(keys[i])); } } +#endif static void* IdFn(void* arg) { return arg; @@ -87,6 +90,15 @@ TEST(pthread, pthread_create) { ASSERT_EQ(expected_result, result); } +TEST(pthread, pthread_create_EAGAIN) { + pthread_attr_t attributes; + ASSERT_EQ(0, pthread_attr_init(&attributes)); + ASSERT_EQ(0, pthread_attr_setstacksize(&attributes, static_cast<size_t>(-1) & ~(getpagesize() - 1))); + + pthread_t t; + ASSERT_EQ(EAGAIN, pthread_create(&t, &attributes, IdFn, NULL)); +} + TEST(pthread, pthread_no_join_after_detach) { pthread_t t1; ASSERT_EQ(0, pthread_create(&t1, NULL, SleepFn, reinterpret_cast<void*>(5))); @@ -174,7 +186,7 @@ TEST(pthread, pthread_sigmask) { ASSERT_EQ(0, reinterpret_cast<int>(join_result)); } -#if !defined(__GLIBC__) +#if __BIONIC__ extern "C" int __pthread_clone(int (*fn)(void*), void* child_stack, int flags, void* arg); TEST(pthread, __pthread_clone) { uintptr_t fake_child_stack[16]; @@ -183,3 +195,27 @@ TEST(pthread, __pthread_clone) { ASSERT_EQ(EINVAL, errno); } #endif + +TEST(pthread, pthread_setname_np__too_long) { + ASSERT_EQ(ERANGE, pthread_setname_np(pthread_self(), "this name is far too long for linux")); +} + +TEST(pthread, pthread_setname_np__self) { + ASSERT_EQ(0, pthread_setname_np(pthread_self(), "short 1")); +} + +TEST(pthread, pthread_setname_np__other) { + pthread_t t1; + ASSERT_EQ(0, pthread_create(&t1, NULL, SleepFn, reinterpret_cast<void*>(5))); + ASSERT_EQ(0, pthread_setname_np(t1, "short 2")); +} + +TEST(pthread, pthread_setname_np__no_such_thread) { + pthread_t t1; + ASSERT_EQ(0, pthread_create(&t1, NULL, IdFn, NULL)); + void* result; + ASSERT_EQ(0, pthread_join(t1, &result)); + + // Call pthread_setname_np after thread has already exited. + ASSERT_EQ(ENOENT, pthread_setname_np(t1, "short 3")); +} |