OSDN Git Service

Revert "Revert "More pthreads cleanup.""
authorElliott Hughes <enh@google.com>
Tue, 12 Feb 2013 16:40:24 +0000 (16:40 +0000)
committerElliott Hughes <enh@google.com>
Tue, 12 Feb 2013 23:27:18 +0000 (15:27 -0800)
This reverts commit 6f94de3ca49e4ea147b1c59e5818fa175846518f

(Doesn't try to increase the number of TLS slots; that leads to
an inability to boot. Adds more tests.)

Change-Id: Ia7d25ba3995219ed6e686463dbba80c95cc831ca

18 files changed:
libc/Android.mk
libc/bionic/dirent.cpp
libc/bionic/pthread.c
libc/bionic/pthread_attr.cpp [new file with mode: 0644]
libc/bionic/pthread_internal.h
libc/bionic/pthread_key.cpp
libc/bionic/pthread_setname_np.cpp [new file with mode: 0644]
libc/bionic/pthread_sigmask.cpp
libc/bionic/strerror_r.cpp
libc/bionic/stubs.cpp
libc/bionic/sysconf.cpp
libc/bionic/tmpfile.cpp
libc/private/ErrnoRestorer.h [new file with mode: 0644]
libc/private/ThreadLocalBuffer.h
libc/private/bionic_futex.h
libc/private/bionic_tls.h
tests/Android.mk
tests/pthread_test.cpp

index 0f61bcf..bf4ca7b 100644 (file)
@@ -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 \
index 3a7b5b4..74297d8 100644 (file)
@@ -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;
 }
 
index e1ace7d..bdf2b87 100644 (file)
  * 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 (file)
index 0000000..831a28e
--- /dev/null
@@ -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;
+}
index 24b420c..63071d9 100644 (file)
@@ -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;
 
index 00dacba..b01f9bd 100644 (file)
  * 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 (file)
index 0000000..88b86ec
--- /dev/null
@@ -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;
+}
index 9c9dc3e..e4e1b2b 100644 (file)
 #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;
 }
index 646cc52..81120ec 100644 (file)
@@ -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;
 }
 
index 3f24d1b..8ddc326 100644 (file)
 #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;
 }
 
index f4845e1..869faef 100644 (file)
@@ -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;
index b97cc6c..8419ff5 100644 (file)
@@ -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 (file)
index 0000000..ed6ab62
--- /dev/null
@@ -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
index 1c5e3f4..e5bd28c 100644 (file)
 // 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) { \
index eb49fb7..6c7fdbe 100644 (file)
@@ -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 */
index edf878f..4dd66c0 100644 (file)
@@ -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);
index 3217a4d..8138cff 100644 (file)
@@ -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)
index 0ccd948..00aa4b1 100644 (file)
@@ -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"));
+}