summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoland McGrath <roland@hack.frob.com>2015-05-26 12:57:15 -0700
committerRoland McGrath <roland@hack.frob.com>2015-05-26 12:57:15 -0700
commit7be88c1bf860f4197e3ffac486515d6f1b38c666 (patch)
tree71b8316f4b2dd2ba296f9ddfd00fa2735e05dd95
parent6afb9c0175006c8060928537842364f83df6fc15 (diff)
downloadglibc-roland/nacl-exit-stacks.tar.gz
-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