about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog14
-rw-r--r--csu/libc-start.c2
-rw-r--r--nptl/pthread_create.c6
-rw-r--r--sysdeps/arm/nacl/exit-thread.c38
-rw-r--r--sysdeps/generic/exit-thread.h12
-rw-r--r--sysdeps/nacl/Makefile2
-rw-r--r--sysdeps/nacl/Versions1
-rw-r--r--sysdeps/nacl/exit-thread.c157
-rw-r--r--sysdeps/nacl/exit-thread.h23
-rw-r--r--sysdeps/unix/sysv/linux/exit-thread.h3
10 files changed, 234 insertions, 24 deletions
diff --git a/ChangeLog b/ChangeLog
index 81ce22e61b..b7e63359f7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2015-05-26  Roland McGrath  <roland@hack.frob.com>
+
+	* sysdeps/nacl/exit-thread.h (__exit_thread): Replace inline
+	function with extern declaration.
+	* sysdeps/nacl/exit-thread.c: New file.
+	* sysdeps/arm/nacl/exit-thread.c: New file.
+	* sysdeps/nacl/Makefile [$(subdir) = csu] (sysdep_routines): Add it.
+	* sysdeps/nacl/Versions (libc: GLIBC_PRIVATE): Add __exit_thread.
+
+	* sysdeps/generic/exit-thread.h (__exit_thread): Take new
+	argument DETACHED.
+	* sysdeps/unix/sysv/linux/exit-thread.h (__exit_thread): Likewise.
+	* csu/libc-start.c (LIBC_START_MAIN): Update caller.
+
 2015-05-26  Andriy Rysin  <arysin@gmail.com>
 
 	[BZ #17293]
diff --git a/csu/libc-start.c b/csu/libc-start.c
index 0afa7c01ba..728769e834 100644
--- a/csu/libc-start.c
+++ b/csu/libc-start.c
@@ -313,7 +313,7 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
 
       if (! atomic_decrement_and_test (ptr))
 	/* Not much left to do but to exit the thread, not the process.  */
-	__exit_thread ();
+	__exit_thread (false);
     }
 #else
   /* Nothing fancy, just call the function.  */
diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
index 71a56193e6..dcca1a5ab9 100644
--- a/nptl/pthread_create.c
+++ b/nptl/pthread_create.c
@@ -433,8 +433,10 @@ START_THREAD_DEFN
   if (freesize > PTHREAD_STACK_MIN)
     __madvise (pd->stackblock, freesize - PTHREAD_STACK_MIN, MADV_DONTNEED);
 
+  const bool detached = IS_DETACHED (pd);
+
   /* If the thread is detached free the TCB.  */
-  if (IS_DETACHED (pd))
+  if (detached)
     /* Free the TCB.  */
     __free_tcb (pd);
   else if (__glibc_unlikely (pd->cancelhandling & SETXID_BITMASK))
@@ -457,7 +459,7 @@ START_THREAD_DEFN
 
      The exit code is zero since in case all threads exit by calling
      'pthread_exit' the exit status must be 0 (zero).  */
-  __exit_thread ();
+  __exit_thread (detached);
 
   /* NOTREACHED */
 }
diff --git a/sysdeps/arm/nacl/exit-thread.c b/sysdeps/arm/nacl/exit-thread.c
new file mode 100644
index 0000000000..9b39dbaa42
--- /dev/null
+++ b/sysdeps/arm/nacl/exit-thread.c
@@ -0,0 +1,38 @@
+/* Call to terminate the current thread.  NaCl/ARM version.
+   Copyright (C) 2015 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* This bit is machine-specific: Switch stacks to SP and call FUNC (ARG).  */
+static void __attribute__ ((noreturn))
+call_on_stack (void *sp,
+               void (*func) (void *)
+                 internal_function __attribute__ ((noreturn)),
+               void *arg)
+{
+  register void *r0 asm ("r0") = arg;
+  asm volatile ("bic sp, %[sp], %[dmask]\n\t"
+                "sfi_blx %[func]"
+                :
+                : [sp] "r" (sp),
+                  [dmask] "ir" (0xc0000000),
+                  [func] "r" (func),
+                  "r" (r0));
+  __builtin_trap ();
+}
+
+/* The rest is machine-independent.  */
+#include <sysdeps/nacl/exit-thread.c>
diff --git a/sysdeps/generic/exit-thread.h b/sysdeps/generic/exit-thread.h
index 787029a8de..d0f1a4639d 100644
--- a/sysdeps/generic/exit-thread.h
+++ b/sysdeps/generic/exit-thread.h
@@ -16,12 +16,16 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-/* This causes the current thread to exit, without affecting other
-   threads in the process if there are any.  If there are no other
-   threads left, then this has the effect of _exit (0).  */
+#include <stdbool.h>
+
+/* This causes the current thread to exit, without affecting other threads
+   in the process if there are any.  If there are no other threads left,
+   then this has the effect of _exit (0).  If DETACHED is true, then the
+   TCB returned by THREAD_SELF has been reclaimed and must not be examined
+   or touched.  */
 
 static inline void __attribute__ ((noreturn, always_inline, unused))
-__exit_thread (void)
+__exit_thread (bool detached)
 {
   while (1)
     asm ("write me!");
diff --git a/sysdeps/nacl/Makefile b/sysdeps/nacl/Makefile
index b51156b5ad..a8e737c340 100644
--- a/sysdeps/nacl/Makefile
+++ b/sysdeps/nacl/Makefile
@@ -108,7 +108,7 @@ test-wrapper-env = $(test-wrapper-env-only)
 test-wrapper = $(test-wrapper-env) --
 
 ifeq ($(subdir),csu)
-sysdep_routines += nacl_interface_query \
+sysdep_routines += nacl_interface_query exit-thread \
 		   nacl-interfaces $(call nacl-routines-of,libc)
 endif
 
diff --git a/sysdeps/nacl/Versions b/sysdeps/nacl/Versions
index 4ac56c2a28..d2b361e032 100644
--- a/sysdeps/nacl/Versions
+++ b/sysdeps/nacl/Versions
@@ -15,6 +15,7 @@ libc {
     __libc_open;
     __libc_close;
     __libc_fork;
+    __exit_thread;
 
     __nacl_irt_*;
   }
diff --git a/sysdeps/nacl/exit-thread.c b/sysdeps/nacl/exit-thread.c
new file mode 100644
index 0000000000..fd3e113161
--- /dev/null
+++ b/sysdeps/nacl/exit-thread.c
@@ -0,0 +1,157 @@
+/* Call to terminate the current thread.  NaCl version.
+   Copyright (C) 2015 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <exit-thread.h>
+#include <lowlevellock.h>
+#include <nacl-interfaces.h>
+#include <pthread-functions.h>
+#include <stdbool.h>
+#include <sysdep.h>
+#include <tls.h>
+#include <atomic.h>
+
+
+/* A sysdeps/CPU/nacl/exit-thread.c file defines this function
+   and then #include's this file.  */
+static void call_on_stack (void *sp,
+			   void (*func) (void *)
+			     internal_function __attribute__ ((noreturn)),
+			   void *arg)
+  __attribute__ ((noreturn));
+
+
+/* This is a trivial wrapper around the OS call to terminate the
+   calling thread.  It just sugar to have a paranoidly definitely
+   noreturn function.  */
+static void __attribute__ ((noreturn))
+do_thread_exit (volatile int32_t *stack_flag)
+{
+  __nacl_irt_thread.thread_exit ((int32_t *) stack_flag);
+
+  /* That never returns unless something is severely and unrecoverably
+     wrong.  If it ever does, try to make sure we crash.  */
+  while (1)
+    __builtin_trap ();
+}
+
+
+/* The complexity implemented here is only necessary when there are
+   actually multiple threads in the program (that is, any other threads
+   that will still exist when this one finishes exiting).  So detect
+   that so as to short-circuit the complexity when it's superfluous.  */
+static bool
+multiple_threads (void)
+{
+#ifdef SHARED
+  unsigned int *ptr_nthreads = __libc_pthread_functions.ptr_nthreads;
+  PTR_DEMANGLE (ptr_nthreads);
+#else
+  extern unsigned int __nptl_nthreads __attribute ((weak));
+  unsigned int *const ptr_nthreads = &__nptl_nthreads;
+  if (ptr_nthreads == NULL)
+    return false;
+#endif
+
+  /* We are called after nthreads has been decremented for the current
+     thread.  So it's the count of threads other than this one.  */
+  return *ptr_nthreads > 0;
+}
+
+
+#define EXIT_STACK_SIZE 256
+
+struct exit_stack
+{
+  char stack[EXIT_STACK_SIZE] __attribute__ ((aligned (16)));
+  volatile int32_t flag;
+};
+typedef struct exit_stack exit_stack_t;
+
+static exit_stack_t initial_exit_stack;
+static int exit_stack_lock = LLL_LOCK_INITIALIZER;
+
+static exit_stack_t *
+get_exit_stack (void)
+{
+  exit_stack_t *result;
+
+  lll_lock (exit_stack_lock, LLL_PRIVATE);
+
+  result = &initial_exit_stack;
+
+  while (atomic_load_relaxed (&result->flag) != 0)
+    {
+      lll_unlock (exit_stack_lock, LLL_PRIVATE);
+      __nacl_irt_basic.sched_yield ();
+      lll_lock (exit_stack_lock, LLL_PRIVATE);
+    }
+
+  atomic_store_relaxed (&result->flag, 1);
+
+  lll_unlock (exit_stack_lock, LLL_PRIVATE);
+
+  return result;
+}
+
+
+/* This is called on the exit stack that is passed as the argument.
+   Its mandate is to clear and futex-wake THREAD_SELF->tid and then
+   exit, eventually releasing the exit stack for reuse.  */
+static void internal_function __attribute__ ((noreturn))
+exit_on_exit_stack (void *arg)
+{
+  exit_stack_t *stack = arg;
+  struct pthread *pd = THREAD_SELF;
+
+  /* Mark the thread as having exited and wake anybody waiting for it.
+     After this, both PD itself and its real stack will be reclaimed
+     and it's not safe to touch or examine them.  */
+  pd->tid = 0;
+  lll_futex_wake (&pd->tid, 1, LLL_PRIVATE);
+
+  /* Now we can exit for real, and get off this exit stack.  The system
+     will clear the flag some time after the exit stack is guaranteed not
+     to be in use.  */
+  do_thread_exit (&stack->flag);
+}
+
+static void __attribute__ ((noreturn))
+exit_off_stack (void)
+{
+  exit_stack_t *stack = get_exit_stack ();
+  void *sp = stack + 1;
+
+  /* This switches onto the exit stack and then performs:
+	exit_on_exit_stack (stack);
+     How to accomplish that is, of course, machine-dependent.  */
+  call_on_stack (sp, &exit_on_exit_stack, stack);
+}
+
+void
+__exit_thread (bool detached)
+{
+  if (detached || !multiple_threads ())
+    /* There is no other thread that cares when we exit, so life is simple.
+       In the DETACHED case, we're not allowed to look at THREAD_SELF any
+       more, so avoiding the complex path is not just an optimization.  */
+    do_thread_exit (NULL);
+  else
+    /* We must exit in a way that wakes up pthread_join,
+       i.e. clears and futex-wakes THREAD_SELF->tid.  */
+    exit_off_stack ();
+}
diff --git a/sysdeps/nacl/exit-thread.h b/sysdeps/nacl/exit-thread.h
index a08a5b1d5c..a67ce3c1d8 100644
--- a/sysdeps/nacl/exit-thread.h
+++ b/sysdeps/nacl/exit-thread.h
@@ -16,20 +16,13 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-#include <stddef.h>
-#include <nacl-interfaces.h>
+#include <stdbool.h>
 
-/* This causes the current thread to exit, without affecting other
-   threads in the process if there are any.  If there are no other
-   threads left, then this has the effect of _exit (0).  */
+/* This causes the current thread to exit, without affecting other threads
+   in the process if there are any.  If there are no other threads left,
+   then this has the effect of _exit (0).  If DETACHED is true, then the
+   TCB returned by THREAD_SELF has been reclaimed and must not be examined
+   or touched.  */
 
-static inline void __attribute__ ((noreturn, always_inline, unused))
-__exit_thread (void)
-{
-  __nacl_irt_thread.thread_exit (NULL);
-
-  /* That never returns unless something is severely and unrecoverably wrong.
-     If it ever does, try to make sure we crash.  */
-  while (1)
-    __builtin_trap ();
-}
+extern void __exit_thread (bool detached)
+  internal_function __attribute__ ((noreturn));
diff --git a/sysdeps/unix/sysv/linux/exit-thread.h b/sysdeps/unix/sysv/linux/exit-thread.h
index e5392f7122..c34269514b 100644
--- a/sysdeps/unix/sysv/linux/exit-thread.h
+++ b/sysdeps/unix/sysv/linux/exit-thread.h
@@ -16,6 +16,7 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <stdbool.h>
 #include <sysdep.h>
 
 /* This causes the current thread to exit, without affecting other
@@ -23,7 +24,7 @@
    threads left, then this has the effect of _exit (0).  */
 
 static inline void __attribute__ ((noreturn, always_inline, unused))
-__exit_thread (void)
+__exit_thread (bool detached __attribute__ ((unused)))
 {
   /* Doing this in a loop is mostly just to satisfy the compiler that the
      function really qualifies as noreturn.  It also means that in some