/* 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
. */
#include
#include
#include
#include
#include
#include
#include
#include
/* 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 ();
}