OSDN Git Service

More pthreads cleanup.
authorElliott Hughes <enh@google.com>
Mon, 11 Feb 2013 20:34:03 +0000 (12:34 -0800)
committerElliott Hughes <enh@google.com>
Mon, 11 Feb 2013 22:56:39 +0000 (14:56 -0800)
POSIX says pthread_create returns EAGAIN, not ENOMEM.

Also pull pthread_attr_t functions into their own file.

Also pull pthread_setname_np into its own file.

Also remove unnecessary #includes from pthread_key.cpp.

Also account for those pthread keys used internally by bionic,
so they don't count against the number of keys available to user
code. (They do with glibc, but glibc's limit is the much more
generous 1024.)

Also factor out the common errno-restoring idiom to reduce gotos.

Bug: 6702535
Change-Id: I555e66efffcf2c1b5a2873569e91489156efca42

17 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/pthread_test.cpp

index d21878a..623bda2 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..703acd6 100644 (file)
 // TODO: move __cxa_guard_acquire and __cxa_guard_release into libc.
 
 #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() { \
+  /* 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); \
   }
 
 // 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..846eb84 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 + TLS_SLOT_FIRST_USER_SLOT + GLOBAL_INIT_THREAD_LOCAL_BUFFER_COUNT)
 
 /* set the Thread Local Storage, must contain at least BIONIC_TLS_SLOTS pointers */
 extern void __init_tls(void**  tls, void*  thread_info);
index 2cf45f3..12ebd1e 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)));