summaryrefslogtreecommitdiffstats
path: root/lib/libthr/thread/thr_exit.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libthr/thread/thr_exit.c')
-rw-r--r--lib/libthr/thread/thr_exit.c309
1 files changed, 309 insertions, 0 deletions
diff --git a/lib/libthr/thread/thr_exit.c b/lib/libthr/thread/thr_exit.c
new file mode 100644
index 0000000..7001311
--- /dev/null
+++ b/lib/libthr/thread/thr_exit.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "namespace.h"
+#include <errno.h>
+#ifdef _PTHREAD_FORCED_UNWIND
+#include <dlfcn.h>
+#endif
+#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"
+
+void _pthread_exit(void *status);
+
+static void exit_thread(void) __dead2;
+
+__weak_reference(_pthread_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,
+ int64_t exc_class,
+ struct _Unwind_Exception *exc_obj,
+ struct _Unwind_Context *context, void *stop_parameter);
+/* unwind library pointers */
+static _Unwind_Reason_Code (*uwl_forcedunwind)(struct _Unwind_Exception *,
+ _Unwind_Stop_Fn, void *);
+static unsigned long (*uwl_getcfa)(struct _Unwind_Context *);
+
+static void
+thread_uw_init(void)
+{
+ static int inited = 0;
+ Dl_info dlinfo;
+ void *handle;
+ void *forcedunwind, *getcfa;
+
+ if (inited)
+ return;
+ handle = RTLD_DEFAULT;
+ if ((forcedunwind = dlsym(handle, "_Unwind_ForcedUnwind")) != NULL) {
+ if (dladdr(forcedunwind, &dlinfo)) {
+ /*
+ * Make sure the address is always valid by holding the library,
+ * also assume functions are in same library.
+ */
+ if ((handle = dlopen(dlinfo.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);
+}
+
+unsigned long
+_Unwind_GetCFA(struct _Unwind_Context *context)
+{
+ return (*uwl_getcfa)(context);
+}
+#else
+#pragma weak _Unwind_GetCFA
+#pragma weak _Unwind_ForcedUnwind
+#endif /* PIC */
+
+static void
+thread_unwind_cleanup(_Unwind_Reason_Code code, struct _Unwind_Exception *e)
+{
+ /*
+ * Specification said that _Unwind_Resume should not be used here,
+ * instead, user should rethrow the exception. For C++ user, they
+ * should put "throw" sentence in catch(...) block.
+ */
+ PANIC("exception should be rethrown");
+}
+
+static _Unwind_Reason_Code
+thread_unwind_stop(int version, _Unwind_Action actions,
+ int64_t exc_class,
+ struct _Unwind_Exception *exc_obj,
+ struct _Unwind_Context *context, void *stop_parameter)
+{
+ struct pthread *curthread = _get_curthread();
+ struct pthread_cleanup *cur;
+ uintptr_t cfa;
+ int done = 0;
+
+ /* XXX assume stack grows down to lower address */
+
+ 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)
+ exit_thread(); /* Never return! */
+
+ 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_exit(const char *fname, int lineno, const char *msg)
+{
+
+ /* Write an error message to the standard error file descriptor: */
+ _thread_printf(2,
+ "Fatal error '%s' at line %d in file %s (errno = %d)\n",
+ msg, lineno, fname, errno);
+
+ abort();
+}
+
+void
+_pthread_exit(void *status)
+{
+ _pthread_exit_mask(status, NULL);
+}
+
+void
+_pthread_exit_mask(void *status, sigset_t *mask)
+{
+ struct pthread *curthread = _get_curthread();
+
+ /* Check if this thread is already in the process of exiting: */
+ if (curthread->cancelling) {
+ char msg[128];
+ snprintf(msg, sizeof(msg), "Thread %p has called "
+ "pthread_exit() from a destructor. POSIX 1003.1 "
+ "1996 s16.2.5.2 does not allow this!", curthread);
+ PANIC(msg);
+ }
+
+ /* Flag this thread as exiting. */
+ 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);
+ }
+
+ /* Save the return value: */
+ curthread->ret = status;
+#ifdef _PTHREAD_FORCED_UNWIND
+
+#ifdef PIC
+ thread_uw_init();
+#endif /* PIC */
+
+#ifdef PIC
+ 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);
+ }
+ exit_thread();
+ }
+
+#else
+ while (curthread->cleanup != NULL) {
+ __pthread_cleanup_pop_imp(1);
+ }
+
+ exit_thread();
+#endif /* _PTHREAD_FORCED_UNWIND */
+}
+
+static void
+exit_thread(void)
+{
+ struct pthread *curthread = _get_curthread();
+
+ /* Check if there is thread specific data: */
+ if (curthread->specific != NULL) {
+ /* Run the thread-specific data destructors: */
+ _thread_cleanupspecific();
+ }
+
+ if (!_thr_isthreaded())
+ exit(0);
+
+ if (atomic_fetchadd_int(&_thread_active_threads, -1) == 1) {
+ exit(0);
+ /* Never reach! */
+ }
+
+ /* Tell malloc that the thread is exiting. */
+ _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);
+ /*
+ * Thread was created with initial refcount 1, we drop the
+ * reference count to allow it to be garbage collected.
+ */
+ curthread->refcount--;
+ _thr_try_gc(curthread, curthread); /* thread lock released */
+
+#if defined(_PTHREADS_INVARIANTS)
+ if (THR_IN_CRITICAL(curthread))
+ PANIC("thread exits with resources held!");
+#endif
+ /*
+ * Kernel will do wakeup at the address, so joiner thread
+ * will be resumed if it is sleeping at the address.
+ */
+ thr_exit(&curthread->tid);
+ PANIC("thr_exit() returned");
+ /* Never reach! */
+}
OpenPOWER on IntegriCloud