#include "namespace.h"
#include <errno.h>
#ifdef _PTHREAD_FORCED_UNWIND
#include <dlfcn.h>
#endif
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/signalvar.h>
#include "un-namespace.h"
#include "libc_private.h"
#include "thr_private.h"
static void exit_thread(void) __dead2;
__weak_reference(_Tthr_exit, pthread_exit);
__weak_reference(_Tthr_exit, _pthread_exit);
#ifdef _PTHREAD_FORCED_UNWIND
static int message_printed;
static void thread_unwind(void) __dead2;
#ifdef PIC
static void thread_uw_init(void);
static _Unwind_Reason_Code thread_unwind_stop(int version,
_Unwind_Action actions,
uint64_t exc_class,
struct _Unwind_Exception *exc_obj,
struct _Unwind_Context *context, void *stop_parameter);
static _Unwind_Reason_Code (*uwl_forcedunwind)(struct _Unwind_Exception *,
_Unwind_Stop_Fn, void *);
static uintptr_t (*uwl_getcfa)(struct _Unwind_Context *);
static void
thread_uw_init(void)
{
static int inited = 0;
Dl_info dli;
void *handle;
void *forcedunwind, *getcfa;
if (inited)
return;
handle = RTLD_DEFAULT;
if ((forcedunwind = dlsym(handle, "_Unwind_ForcedUnwind")) != NULL) {
if (dladdr(forcedunwind, &dli)) {
if ((handle = dlopen(dli.dli_fname, RTLD_LAZY)) != NULL) {
forcedunwind = dlsym(handle, "_Unwind_ForcedUnwind");
getcfa = dlsym(handle, "_Unwind_GetCFA");
if (forcedunwind != NULL && getcfa != NULL) {
uwl_getcfa = getcfa;
atomic_store_rel_ptr((volatile void *)&uwl_forcedunwind,
(uintptr_t)forcedunwind);
} else {
dlclose(handle);
}
}
}
}
inited = 1;
}
_Unwind_Reason_Code
_Unwind_ForcedUnwind(struct _Unwind_Exception *ex, _Unwind_Stop_Fn stop_func,
void *stop_arg)
{
return (*uwl_forcedunwind)(ex, stop_func, stop_arg);
}
uintptr_t
_Unwind_GetCFA(struct _Unwind_Context *context)
{
return (*uwl_getcfa)(context);
}
#else
#pragma weak _Unwind_GetCFA
#pragma weak _Unwind_ForcedUnwind
#endif
static void
thread_unwind_cleanup(_Unwind_Reason_Code code __unused,
struct _Unwind_Exception *e __unused)
{
PANIC("exception should be rethrown");
}
static _Unwind_Reason_Code
thread_unwind_stop(int version __unused, _Unwind_Action actions,
uint64_t exc_class __unused,
struct _Unwind_Exception *exc_obj __unused,
struct _Unwind_Context *context, void *stop_parameter __unused)
{
struct pthread *curthread = _get_curthread();
struct pthread_cleanup *cur;
uintptr_t cfa;
int done = 0;
cfa = _Unwind_GetCFA(context);
if (actions & _UA_END_OF_STACK ||
cfa >= (uintptr_t)curthread->unwind_stackend) {
done = 1;
}
while ((cur = curthread->cleanup) != NULL &&
(done || (uintptr_t)cur <= cfa)) {
__pthread_cleanup_pop_imp(1);
}
if (done) {
__cxa_thread_call_dtors();
exit_thread();
}
return (_URC_NO_REASON);
}
static void
thread_unwind(void)
{
struct pthread *curthread = _get_curthread();
curthread->ex.exception_class = 0;
curthread->ex.exception_cleanup = thread_unwind_cleanup;
_Unwind_ForcedUnwind(&curthread->ex, thread_unwind_stop, NULL);
PANIC("_Unwind_ForcedUnwind returned");
}
#endif
void
_thread_exitf(const char *fname, int lineno, const char *fmt, ...)
{
va_list ap;
_thread_printf(STDERR_FILENO, "Fatal error '");
va_start(ap, fmt);
_thread_vprintf(STDERR_FILENO, fmt, ap);
va_end(ap);
_thread_printf(STDERR_FILENO, "' at line %d in file %s (errno = %d)\n",
lineno, fname, errno);
abort();
}
void
_thread_exit(const char *fname, int lineno, const char *msg)
{
_thread_exitf(fname, lineno, "%s", msg);
}
void
_Tthr_exit(void *status)
{
_pthread_exit_mask(status, NULL);
}
void
_pthread_exit_mask(void *status, sigset_t *mask)
{
struct pthread *curthread = _get_curthread();
if (curthread->cancelling)
PANIC("Thread %p has called "
"pthread_exit() from a destructor. POSIX 1003.1 "
"1996 s16.2.5.2 does not allow this!", curthread);
curthread->cancelling = 1;
curthread->no_cancel = 1;
curthread->cancel_async = 0;
curthread->cancel_point = 0;
if (mask != NULL)
__sys_sigprocmask(SIG_SETMASK, mask, NULL);
if (curthread->unblock_sigcancel) {
sigset_t set;
curthread->unblock_sigcancel = 0;
SIGEMPTYSET(set);
SIGADDSET(set, SIGCANCEL);
__sys_sigprocmask(SIG_UNBLOCK, mask, NULL);
}
curthread->ret = status;
#ifdef _PTHREAD_FORCED_UNWIND
#ifdef PIC
thread_uw_init();
if (uwl_forcedunwind != NULL) {
#else
if (_Unwind_ForcedUnwind != NULL) {
#endif
if (curthread->unwind_disabled) {
if (message_printed == 0) {
message_printed = 1;
_thread_printf(2, "Warning: old _pthread_cleanup_push was called, "
"stack unwinding is disabled.\n");
}
goto cleanup;
}
thread_unwind();
} else {
cleanup:
while (curthread->cleanup != NULL) {
__pthread_cleanup_pop_imp(1);
}
__cxa_thread_call_dtors();
exit_thread();
}
#else
while (curthread->cleanup != NULL) {
__pthread_cleanup_pop_imp(1);
}
__cxa_thread_call_dtors();
exit_thread();
#endif
}
static void
exit_thread(void)
{
struct pthread *curthread = _get_curthread();
free(curthread->name);
curthread->name = NULL;
if (curthread->specific != NULL) {
_thread_cleanupspecific();
}
if (!_thr_isthreaded())
exit(0);
if (atomic_fetchadd_int(&_thread_active_threads, -1) == 1) {
exit(0);
}
_malloc_thread_cleanup();
THR_LOCK(curthread);
curthread->state = PS_DEAD;
if (curthread->flags & THR_FLAGS_NEED_SUSPEND) {
curthread->cycle++;
_thr_umtx_wake(&curthread->cycle, INT_MAX, 0);
}
if (!curthread->force_exit && SHOULD_REPORT_EVENT(curthread, TD_DEATH))
_thr_report_death(curthread);
curthread->refcount--;
_thr_try_gc(curthread, curthread);
#if defined(_PTHREADS_INVARIANTS)
if (THR_IN_CRITICAL(curthread))
PANIC("thread %p exits with resources held!", curthread);
#endif
thr_exit(&curthread->tid);
PANIC("thr_exit() returned");
}