# Via Android Git Automerger (1) and others * commit 'a2a9817f4c08d1f56d90f02ed13f531e8093f8e8': More pthreads cleanup.
This commit is contained in:
commit
23731e841a
17 changed files with 363 additions and 292 deletions
|
@ -279,6 +279,8 @@ libc_bionic_src_files := \
|
||||||
bionic/__memcpy_chk.cpp \
|
bionic/__memcpy_chk.cpp \
|
||||||
bionic/__memmove_chk.cpp \
|
bionic/__memmove_chk.cpp \
|
||||||
bionic/__memset_chk.cpp \
|
bionic/__memset_chk.cpp \
|
||||||
|
bionic/pthread_attr.cpp \
|
||||||
|
bionic/pthread_setname_np.cpp \
|
||||||
bionic/pthread_sigmask.cpp \
|
bionic/pthread_sigmask.cpp \
|
||||||
bionic/raise.cpp \
|
bionic/raise.cpp \
|
||||||
bionic/sbrk.cpp \
|
bionic/sbrk.cpp \
|
||||||
|
|
|
@ -36,7 +36,8 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <private/ScopedPthreadMutexLocker.h>
|
#include "private/ErrnoRestorer.h"
|
||||||
|
#include "private/ScopedPthreadMutexLocker.h"
|
||||||
|
|
||||||
struct DIR {
|
struct DIR {
|
||||||
int fd_;
|
int fd_;
|
||||||
|
@ -108,7 +109,7 @@ dirent* readdir(DIR* d) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int readdir_r(DIR* d, dirent* entry, dirent** result) {
|
int readdir_r(DIR* d, dirent* entry, dirent** result) {
|
||||||
int saved_errno = errno;
|
ErrnoRestorer errno_restorer;
|
||||||
|
|
||||||
*result = NULL;
|
*result = NULL;
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
@ -124,7 +125,6 @@ int readdir_r(DIR* d, dirent* entry, dirent** result) {
|
||||||
memcpy(entry, next, next->d_reclen);
|
memcpy(entry, next, next->d_reclen);
|
||||||
*result = entry;
|
*result = entry;
|
||||||
}
|
}
|
||||||
errno = saved_errno;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,23 +26,12 @@
|
||||||
* SUCH DAMAGE.
|
* SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <malloc.h>
|
|
||||||
#include <memory.h>
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <signal.h>
|
|
||||||
#include <stdint.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <limits.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/atomics.h>
|
#include <sys/atomics.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/prctl.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "bionic_atomic_inline.h"
|
#include "bionic_atomic_inline.h"
|
||||||
|
@ -84,23 +73,8 @@ void ATTRIBUTES _thread_created_hook(pid_t thread_id);
|
||||||
|
|
||||||
static const int kPthreadInitFailed = 1;
|
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 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_internal_t* gThreadList = NULL;
|
||||||
__LIBC_HIDDEN__ pthread_mutex_t gThreadListLock = PTHREAD_MUTEX_INITIALIZER;
|
__LIBC_HIDDEN__ pthread_mutex_t gThreadListLock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
static pthread_mutex_t gDebuggerNotificationLock = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t gDebuggerNotificationLock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
@ -306,7 +280,7 @@ int pthread_create(pthread_t *thread_out, pthread_attr_t const * attr,
|
||||||
|
|
||||||
pthread_internal_t* thread = calloc(sizeof(*thread), 1);
|
pthread_internal_t* thread = calloc(sizeof(*thread), 1);
|
||||||
if (thread == NULL) {
|
if (thread == NULL) {
|
||||||
return ENOMEM;
|
return EAGAIN;
|
||||||
}
|
}
|
||||||
thread->allocated_on_heap = true;
|
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);
|
stack = mkstack(stack_size, attr->guard_size);
|
||||||
if (stack == NULL) {
|
if (stack == NULL) {
|
||||||
free(thread);
|
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
|
/* CAVEAT: our implementation of pthread_cleanup_push/pop doesn't support C++ exceptions
|
||||||
* and thread cancelation
|
* and thread cancelation
|
||||||
*/
|
*/
|
||||||
|
@ -1881,57 +1709,6 @@ int pthread_once( pthread_once_t* once_control, void (*init_routine)(void) )
|
||||||
return 0;
|
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.
|
/* Return the kernel thread ID for a pthread.
|
||||||
* This is only defined for implementations where pthread <-> kernel is 1:1, which this is.
|
* 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.
|
* Not the same as pthread_getthreadid_np, which is commonly defined to be opaque.
|
||||||
|
|
165
libc/bionic/pthread_attr.cpp
Normal file
165
libc/bionic/pthread_attr.cpp
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -62,6 +62,10 @@ pthread_internal_t* __get_thread(void);
|
||||||
|
|
||||||
__LIBC_HIDDEN__ void pthread_key_clean_all(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_internal_t* gThreadList;
|
||||||
extern pthread_mutex_t gThreadListLock;
|
extern pthread_mutex_t gThreadListLock;
|
||||||
|
|
||||||
|
|
|
@ -26,33 +26,10 @@
|
||||||
* SUCH DAMAGE.
|
* SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <malloc.h>
|
|
||||||
#include <memory.h>
|
|
||||||
#include <pthread.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 "bionic_tls.h"
|
||||||
#include "debug_format.h"
|
|
||||||
#include "pthread_internal.h"
|
#include "pthread_internal.h"
|
||||||
#include "thread_private.h"
|
|
||||||
|
|
||||||
/* A technical note regarding our thread-local-storage (TLS) implementation:
|
/* A technical note regarding our thread-local-storage (TLS) implementation:
|
||||||
*
|
*
|
||||||
|
|
79
libc/bionic/pthread_setname_np.cpp
Normal file
79
libc/bionic/pthread_setname_np.cpp
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -30,12 +30,13 @@
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <signal.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);
|
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 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
|
// '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
|
// 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;
|
kernel_sigset_t out_set;
|
||||||
int result = __rt_sigprocmask(how, in_set_ptr, &out_set, sizeof(out_set));
|
if (__rt_sigprocmask(how, in_set_ptr, &out_set, sizeof(out_set)) == -1) {
|
||||||
if (result < 0) {
|
return errno;
|
||||||
result = errno;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oset != NULL) {
|
if (oset != NULL) {
|
||||||
*oset = out_set.bionic;
|
*oset = out_set.bionic;
|
||||||
}
|
}
|
||||||
|
|
||||||
errno = old_errno;
|
return 0;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "private/ErrnoRestorer.h"
|
||||||
|
|
||||||
struct Pair {
|
struct Pair {
|
||||||
int code;
|
int code;
|
||||||
const char* msg;
|
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 strerror_r(int error_number, char* buf, size_t buf_len) {
|
||||||
int saved_errno = errno;
|
ErrnoRestorer errno_restorer;
|
||||||
size_t length;
|
size_t length;
|
||||||
|
|
||||||
const char* error_name = __strerror_lookup(error_number);
|
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);
|
length = snprintf(buf, buf_len, "Unknown error %d", error_number);
|
||||||
}
|
}
|
||||||
if (length >= buf_len) {
|
if (length >= buf_len) {
|
||||||
errno = ERANGE;
|
errno_restorer.override(ERANGE);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
errno = saved_errno;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,15 +31,17 @@
|
||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
#include <mntent.h>
|
#include <mntent.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <private/android_filesystem_config.h>
|
|
||||||
#include <private/debug_format.h>
|
|
||||||
#include <private/logd.h>
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.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.
|
// Thread-specific state for the non-reentrant functions.
|
||||||
static pthread_once_t stubs_once = PTHREAD_ONCE_INIT;
|
static pthread_once_t stubs_once = PTHREAD_ONCE_INIT;
|
||||||
static pthread_key_t stubs_key;
|
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) {
|
passwd** result) {
|
||||||
// getpwnam_r and getpwuid_r don't modify errno, but library calls we
|
// getpwnam_r and getpwuid_r don't modify errno, but library calls we
|
||||||
// make might.
|
// make might.
|
||||||
int old_errno = errno;
|
ErrnoRestorer errno_restorer;
|
||||||
*result = NULL;
|
*result = NULL;
|
||||||
|
|
||||||
// Our implementation of getpwnam(3) and getpwuid(3) use thread-local
|
// 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.
|
// POSIX allows failure to find a match to be considered a non-error.
|
||||||
// Reporting success (0) but with *result NULL is glibc's behavior.
|
// Reporting success (0) but with *result NULL is glibc's behavior.
|
||||||
if (src == NULL) {
|
if (src == NULL) {
|
||||||
int rc = (errno == ENOENT) ? 0 : errno;
|
return (errno == ENOENT) ? 0 : errno;
|
||||||
errno = old_errno;
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Work out where our strings will go in 'buf', and whether we've got
|
// 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;
|
dst->pw_shell = buf + required_byte_count;
|
||||||
required_byte_count += strlen(src->pw_shell) + 1;
|
required_byte_count += strlen(src->pw_shell) + 1;
|
||||||
if (byte_count < required_byte_count) {
|
if (byte_count < required_byte_count) {
|
||||||
errno = old_errno;
|
|
||||||
return ERANGE;
|
return ERANGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the strings.
|
// Copy the strings.
|
||||||
snprintf(buf, byte_count, "%s%c%s%c%s",
|
snprintf(buf, byte_count, "%s%c%s%c%s", src->pw_name, 0, src->pw_dir, 0, src->pw_shell);
|
||||||
src->pw_name, 0, src->pw_dir, 0, src->pw_shell);
|
|
||||||
|
|
||||||
// pw_passwd is non-POSIX and unused (always NULL) in bionic.
|
// pw_passwd is non-POSIX and unused (always NULL) in bionic.
|
||||||
// pw_gecos is non-POSIX and missing 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;
|
dst->pw_uid = src->pw_uid;
|
||||||
|
|
||||||
*result = dst;
|
*result = dst;
|
||||||
errno = old_errno;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -306,7 +306,7 @@ int sysconf(int name) {
|
||||||
return _POSIX_THREAD_DESTRUCTOR_ITERATIONS;
|
return _POSIX_THREAD_DESTRUCTOR_ITERATIONS;
|
||||||
|
|
||||||
case _SC_THREAD_KEYS_MAX:
|
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_STACK_MIN: return SYSTEM_THREAD_STACK_MIN;
|
||||||
case _SC_THREAD_THREADS_MAX: return SYSTEM_THREAD_THREADS_MAX;
|
case _SC_THREAD_THREADS_MAX: return SYSTEM_THREAD_THREADS_MAX;
|
||||||
|
|
|
@ -38,6 +38,8 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "private/ErrnoRestorer.h"
|
||||||
|
|
||||||
class ScopedSignalBlocker {
|
class ScopedSignalBlocker {
|
||||||
public:
|
public:
|
||||||
ScopedSignalBlocker() {
|
ScopedSignalBlocker() {
|
||||||
|
@ -77,9 +79,8 @@ static FILE* __tmpfile_dir(const char* tmp_dir) {
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
int rc = fstat(fd, &sb);
|
int rc = fstat(fd, &sb);
|
||||||
if (rc == -1) {
|
if (rc == -1) {
|
||||||
int old_errno = errno;
|
ErrnoRestorer errno_restorer;
|
||||||
close(fd);
|
close(fd);
|
||||||
errno = old_errno;
|
|
||||||
return NULL;
|
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.
|
// Failure. Clean up. We already unlinked, so we just need to close.
|
||||||
int old_errno = errno;
|
ErrnoRestorer errno_restorer;
|
||||||
close(fd);
|
close(fd);
|
||||||
errno = old_errno;
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
43
libc/private/ErrnoRestorer.h
Normal file
43
libc/private/ErrnoRestorer.h
Normal file
|
@ -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
|
|
@ -37,18 +37,17 @@
|
||||||
// TODO: move __cxa_guard_acquire and __cxa_guard_release into libc.
|
// TODO: move __cxa_guard_acquire and __cxa_guard_release into libc.
|
||||||
|
|
||||||
#define GLOBAL_INIT_THREAD_LOCAL_BUFFER(name) \
|
#define GLOBAL_INIT_THREAD_LOCAL_BUFFER(name) \
|
||||||
static pthread_once_t __bionic_tls_ ## name ## _once; \
|
|
||||||
static pthread_key_t __bionic_tls_ ## name ## _key; \
|
static pthread_key_t __bionic_tls_ ## name ## _key; \
|
||||||
static void __bionic_tls_ ## name ## _key_destroy(void* buffer) { \
|
static void __bionic_tls_ ## name ## _key_destroy(void* buffer) { \
|
||||||
free(buffer); \
|
free(buffer); \
|
||||||
} \
|
} \
|
||||||
static void __bionic_tls_ ## name ## _key_init() { \
|
/* Run automatically when libc.so is opened by the dynamic linker. */ \
|
||||||
|
__attribute__((constructor)) static void __bionic_tls_ ## name ## _key_init() { \
|
||||||
pthread_key_create(&__bionic_tls_ ## name ## _key, __bionic_tls_ ## name ## _key_destroy); \
|
pthread_key_create(&__bionic_tls_ ## name ## _key, __bionic_tls_ ## name ## _key_destroy); \
|
||||||
}
|
}
|
||||||
|
|
||||||
// Leaves "name_tls_buffer" and "name_tls_buffer_size" defined and initialized.
|
// Leaves "name_tls_buffer" and "name_tls_buffer_size" defined and initialized.
|
||||||
#define LOCAL_INIT_THREAD_LOCAL_BUFFER(type, name, byte_count) \
|
#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 = \
|
type name ## _tls_buffer = \
|
||||||
reinterpret_cast<type>(pthread_getspecific(__bionic_tls_ ## name ## _key)); \
|
reinterpret_cast<type>(pthread_getspecific(__bionic_tls_ ## name ## _key)); \
|
||||||
if (name ## _tls_buffer == NULL) { \
|
if (name ## _tls_buffer == NULL) { \
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
|
|
||||||
#include <linux/futex.h>
|
#include <linux/futex.h>
|
||||||
|
|
||||||
|
__BEGIN_DECLS
|
||||||
|
|
||||||
extern int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout);
|
extern int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout);
|
||||||
extern int __futex_wake(volatile void *ftx, int count);
|
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_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);
|
extern int __futex_wait_ex(volatile void *ftx, int pshared, int val, const struct timespec *timeout);
|
||||||
|
|
||||||
|
__END_DECLS
|
||||||
|
|
||||||
#endif /* _BIONIC_FUTEX_H */
|
#endif /* _BIONIC_FUTEX_H */
|
||||||
|
|
|
@ -43,28 +43,40 @@ __BEGIN_DECLS
|
||||||
** pre-allocated slot directly for performance reason).
|
** 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. */
|
/* Well-known TLS slots. What data goes in which slot is arbitrary unless otherwise noted. */
|
||||||
enum {
|
enum {
|
||||||
TLS_SLOT_SELF = 0, /* The kernel requires this specific slot for x86. */
|
TLS_SLOT_SELF = 0, /* The kernel requires this specific slot for x86. */
|
||||||
TLS_SLOT_THREAD_ID,
|
TLS_SLOT_THREAD_ID,
|
||||||
TLS_SLOT_ERRNO,
|
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_API = 3,
|
||||||
TLS_SLOT_OPENGL = 4,
|
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_STACK_GUARD = 5, /* GCC requires this specific slot for x86. */
|
||||||
TLS_SLOT_DLERROR,
|
TLS_SLOT_DLERROR,
|
||||||
|
|
||||||
TLS_SLOT_FIRST_USER_SLOT /* Must come last! */
|
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
|
* Maximum number of elements in the TLS array.
|
||||||
* function will then clear it. Since its use is extremely temporary,
|
* POSIX says this must be at least 128, but Android has traditionally had only 64, minus those
|
||||||
* we reuse an existing location that isn't needed during libc startup.
|
* 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 + TLS_SLOT_FIRST_USER_SLOT + GLOBAL_INIT_THREAD_LOCAL_BUFFER_COUNT)
|
||||||
|
|
||||||
/* set the Thread Local Storage, must contain at least BIONIC_TLS_SLOTS pointers */
|
/* set the Thread Local Storage, must contain at least BIONIC_TLS_SLOTS pointers */
|
||||||
extern void __init_tls(void** tls, void* thread_info);
|
extern void __init_tls(void** tls, void* thread_info);
|
||||||
|
|
|
@ -28,12 +28,14 @@ TEST(pthread, pthread_key_create) {
|
||||||
ASSERT_EQ(EINVAL, pthread_key_delete(key));
|
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) {
|
TEST(pthread, pthread_key_create_lots) {
|
||||||
// We can allocate _SC_THREAD_KEYS_MAX keys.
|
// We can allocate _SC_THREAD_KEYS_MAX keys.
|
||||||
std::vector<pthread_key_t> keys;
|
std::vector<pthread_key_t> keys;
|
||||||
for (int i = 0; i < sysconf(_SC_THREAD_KEYS_MAX); ++i) {
|
for (int i = 0; i < sysconf(_SC_THREAD_KEYS_MAX); ++i) {
|
||||||
pthread_key_t key;
|
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);
|
keys.push_back(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +48,7 @@ TEST(pthread, pthread_key_create_lots) {
|
||||||
ASSERT_EQ(0, pthread_key_delete(keys[i]));
|
ASSERT_EQ(0, pthread_key_delete(keys[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void* IdFn(void* arg) {
|
static void* IdFn(void* arg) {
|
||||||
return arg;
|
return arg;
|
||||||
|
@ -87,6 +90,15 @@ TEST(pthread, pthread_create) {
|
||||||
ASSERT_EQ(expected_result, result);
|
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) {
|
TEST(pthread, pthread_no_join_after_detach) {
|
||||||
pthread_t t1;
|
pthread_t t1;
|
||||||
ASSERT_EQ(0, pthread_create(&t1, NULL, SleepFn, reinterpret_cast<void*>(5)));
|
ASSERT_EQ(0, pthread_create(&t1, NULL, SleepFn, reinterpret_cast<void*>(5)));
|
||||||
|
|
Loading…
Reference in a new issue