1bb535300SJeff Roberson /* 2bb535300SJeff Roberson * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> 3bb535300SJeff Roberson * All rights reserved. 4bb535300SJeff Roberson * 5bb535300SJeff Roberson * Redistribution and use in source and binary forms, with or without 6bb535300SJeff Roberson * modification, are permitted provided that the following conditions 7bb535300SJeff Roberson * are met: 8bb535300SJeff Roberson * 1. Redistributions of source code must retain the above copyright 9bb535300SJeff Roberson * notice, this list of conditions and the following disclaimer. 10bb535300SJeff Roberson * 2. Redistributions in binary form must reproduce the above copyright 11bb535300SJeff Roberson * notice, this list of conditions and the following disclaimer in the 12bb535300SJeff Roberson * documentation and/or other materials provided with the distribution. 13fed32d75SWarner Losh * 3. Neither the name of the author nor the names of any co-contributors 14bb535300SJeff Roberson * may be used to endorse or promote products derived from this software 15bb535300SJeff Roberson * without specific prior written permission. 16bb535300SJeff Roberson * 17bb535300SJeff Roberson * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND 18bb535300SJeff Roberson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19bb535300SJeff Roberson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20bb535300SJeff Roberson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21bb535300SJeff Roberson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22bb535300SJeff Roberson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23bb535300SJeff Roberson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24bb535300SJeff Roberson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25bb535300SJeff Roberson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26bb535300SJeff Roberson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27bb535300SJeff Roberson * SUCH DAMAGE. 28bb535300SJeff Roberson * 29bb535300SJeff Roberson * $FreeBSD$ 30bb535300SJeff Roberson */ 31a091d823SDavid Xu 32e03efb02SRuslan Ermilov #include "namespace.h" 33bb535300SJeff Roberson #include <errno.h> 343832fd24SDavid Xu #ifdef _PTHREAD_FORCED_UNWIND 353832fd24SDavid Xu #include <dlfcn.h> 363832fd24SDavid Xu #endif 37bb535300SJeff Roberson #include <stdio.h> 38bb535300SJeff Roberson #include <stdlib.h> 39bb535300SJeff Roberson #include <pthread.h> 4002c3c858SDavid Xu #include <sys/types.h> 4102c3c858SDavid Xu #include <sys/signalvar.h> 42e03efb02SRuslan Ermilov #include "un-namespace.h" 43a091d823SDavid Xu 44d6742bfbSJason Evans #include "libc_private.h" 45bb535300SJeff Roberson #include "thr_private.h" 46bb535300SJeff Roberson 47a091d823SDavid Xu void _pthread_exit(void *status); 48a091d823SDavid Xu 493832fd24SDavid Xu static void exit_thread(void) __dead2; 503832fd24SDavid Xu 51bb535300SJeff Roberson __weak_reference(_pthread_exit, pthread_exit); 52bb535300SJeff Roberson 533832fd24SDavid Xu #ifdef _PTHREAD_FORCED_UNWIND 543832fd24SDavid Xu 553832fd24SDavid Xu static void thread_unwind(void) __dead2; 563832fd24SDavid Xu #ifdef PIC 573832fd24SDavid Xu static void thread_uw_init(void); 583832fd24SDavid Xu static _Unwind_Reason_Code thread_unwind_stop(int version, 593832fd24SDavid Xu _Unwind_Action actions, 603832fd24SDavid Xu _Unwind_Exception_Class exc_class, 613832fd24SDavid Xu struct _Unwind_Exception *exc_obj, 623832fd24SDavid Xu struct _Unwind_Context *context, void *stop_parameter); 633832fd24SDavid Xu /* unwind library pointers */ 643832fd24SDavid Xu static _Unwind_Reason_Code (*uwl_forcedunwind)(struct _Unwind_Exception *, 653832fd24SDavid Xu _Unwind_Stop_Fn, void *); 663832fd24SDavid Xu static void (*uwl_resume)(struct _Unwind_Exception *exc); 673832fd24SDavid Xu static _Unwind_Word (*uwl_getcfa)(struct _Unwind_Context *); 683832fd24SDavid Xu 693832fd24SDavid Xu static void 703832fd24SDavid Xu thread_uw_init(void) 713832fd24SDavid Xu { 723832fd24SDavid Xu static int inited = 0; 73*4da1da4bSDavid Xu Dl_info dlinfo; 743832fd24SDavid Xu void *handle; 75*4da1da4bSDavid Xu void *forcedunwind, *resume, *getcfa; 763832fd24SDavid Xu 773832fd24SDavid Xu if (inited) 783832fd24SDavid Xu return; 793832fd24SDavid Xu handle = RTLD_DEFAULT; 80*4da1da4bSDavid Xu if ((forcedunwind = dlsym(handle, "_Unwind_ForcedUnwind")) != NULL) { 81*4da1da4bSDavid Xu if (dladdr(forcedunwind, &dlinfo)) { 82*4da1da4bSDavid Xu if ((handle = dlopen(dlinfo.dli_fname, RTLD_LAZY)) != NULL) { 83*4da1da4bSDavid Xu forcedunwind = dlsym(handle, "_Unwind_ForcedUnwind"); 84*4da1da4bSDavid Xu resume = dlsym(handle, "_Unwind_Resume"); 85*4da1da4bSDavid Xu getcfa = dlsym(handle, "_Unwind_GetCFA"); 86*4da1da4bSDavid Xu if (forcedunwind != NULL && resume != NULL && 87*4da1da4bSDavid Xu getcfa != NULL) { 88*4da1da4bSDavid Xu uwl_forcedunwind = forcedunwind; 89*4da1da4bSDavid Xu uwl_resume = resume; 90*4da1da4bSDavid Xu uwl_getcfa = getcfa; 91*4da1da4bSDavid Xu } else { 92*4da1da4bSDavid Xu dlclose(handle); 933832fd24SDavid Xu } 943832fd24SDavid Xu } 95*4da1da4bSDavid Xu } 96*4da1da4bSDavid Xu } 97*4da1da4bSDavid Xu inited = 1; 98*4da1da4bSDavid Xu } 993832fd24SDavid Xu 1003832fd24SDavid Xu void 1013832fd24SDavid Xu _Unwind_Resume(struct _Unwind_Exception *ex) 1023832fd24SDavid Xu { 1033832fd24SDavid Xu (*uwl_resume)(ex); 1043832fd24SDavid Xu } 1053832fd24SDavid Xu 1063832fd24SDavid Xu _Unwind_Reason_Code 1073832fd24SDavid Xu _Unwind_ForcedUnwind(struct _Unwind_Exception *ex, _Unwind_Stop_Fn stop_func, 1083832fd24SDavid Xu void *stop_arg) 1093832fd24SDavid Xu { 1103832fd24SDavid Xu return (*uwl_forcedunwind)(ex, stop_func, stop_arg); 1113832fd24SDavid Xu } 1123832fd24SDavid Xu 1133832fd24SDavid Xu _Unwind_Word 1143832fd24SDavid Xu _Unwind_GetCFA(struct _Unwind_Context *context) 1153832fd24SDavid Xu { 1163832fd24SDavid Xu return (*uwl_getcfa)(context); 1173832fd24SDavid Xu } 1183832fd24SDavid Xu #else 1193832fd24SDavid Xu #pragma weak _Unwind_GetCFA 1203832fd24SDavid Xu #pragma weak _Unwind_ForcedUnwind 1213832fd24SDavid Xu #pragma weak _Unwind_Resume 1223832fd24SDavid Xu #endif /* PIC */ 1233832fd24SDavid Xu 1243832fd24SDavid Xu static void 1253832fd24SDavid Xu thread_unwind_cleanup(_Unwind_Reason_Code code, struct _Unwind_Exception *e) 1263832fd24SDavid Xu { 1273832fd24SDavid Xu /* 1283832fd24SDavid Xu * Specification said that _Unwind_Resume should not be used here, 1293832fd24SDavid Xu * instead, user should rethrow the exception. For C++ user, they 1303832fd24SDavid Xu * should put "throw" sentence in catch(...) block. 1313832fd24SDavid Xu */ 1323832fd24SDavid Xu PANIC("exception should be rethrown"); 1333832fd24SDavid Xu } 1343832fd24SDavid Xu 1353832fd24SDavid Xu static _Unwind_Reason_Code 1363832fd24SDavid Xu thread_unwind_stop(int version, _Unwind_Action actions, 1373832fd24SDavid Xu _Unwind_Exception_Class exc_class, 1383832fd24SDavid Xu struct _Unwind_Exception *exc_obj, 1393832fd24SDavid Xu struct _Unwind_Context *context, void *stop_parameter) 1403832fd24SDavid Xu { 1413832fd24SDavid Xu struct pthread *curthread = _get_curthread(); 1423832fd24SDavid Xu struct pthread_cleanup *cur; 1433832fd24SDavid Xu uintptr_t cfa; 1443832fd24SDavid Xu int done = 0; 1453832fd24SDavid Xu 1463832fd24SDavid Xu /* XXX assume stack grows down to lower address */ 1473832fd24SDavid Xu 1483832fd24SDavid Xu cfa = _Unwind_GetCFA(context); 1493832fd24SDavid Xu if (actions & _UA_END_OF_STACK) { 1503832fd24SDavid Xu done = 1; 1513832fd24SDavid Xu } else if (cfa >= (uintptr_t)curthread->unwind_stackend) { 1523832fd24SDavid Xu done = 1; 1533832fd24SDavid Xu } 1543832fd24SDavid Xu 1553832fd24SDavid Xu while ((cur = curthread->cleanup) != NULL && 1563832fd24SDavid Xu (done || 1573832fd24SDavid Xu ((uintptr_t)cur < (uintptr_t)curthread->unwind_stackend && 1583832fd24SDavid Xu (uintptr_t)cur >= cfa))) { 1593832fd24SDavid Xu __pthread_cleanup_pop_imp(1); 1603832fd24SDavid Xu } 1613832fd24SDavid Xu 1623832fd24SDavid Xu if (done) 1633832fd24SDavid Xu exit_thread(); /* Never return! */ 1643832fd24SDavid Xu 1653832fd24SDavid Xu return (_URC_NO_REASON); 1663832fd24SDavid Xu } 1673832fd24SDavid Xu 1683832fd24SDavid Xu static void 1693832fd24SDavid Xu thread_unwind(void) 1703832fd24SDavid Xu { 1713832fd24SDavid Xu struct pthread *curthread = _get_curthread(); 1723832fd24SDavid Xu 1733832fd24SDavid Xu curthread->ex.exception_class = 0; 1743832fd24SDavid Xu curthread->ex.exception_cleanup = thread_unwind_cleanup; 1753832fd24SDavid Xu _Unwind_ForcedUnwind(&curthread->ex, thread_unwind_stop, NULL); 1763832fd24SDavid Xu PANIC("_Unwind_ForcedUnwind returned"); 1773832fd24SDavid Xu } 1783832fd24SDavid Xu 1793832fd24SDavid Xu #endif 1803832fd24SDavid Xu 181bb535300SJeff Roberson void 18237a6356bSDavid Xu _thread_exit(const char *fname, int lineno, const char *msg) 183bb535300SJeff Roberson { 184bb535300SJeff Roberson 185a091d823SDavid Xu /* Write an error message to the standard error file descriptor: */ 186a091d823SDavid Xu _thread_printf(2, 187bb535300SJeff Roberson "Fatal error '%s' at line %d in file %s (errno = %d)\n", 188a091d823SDavid Xu msg, lineno, fname, errno); 189bb535300SJeff Roberson 190bb535300SJeff Roberson abort(); 191bb535300SJeff Roberson } 192bb535300SJeff Roberson 193bb535300SJeff Roberson void 194bb535300SJeff Roberson _pthread_exit(void *status) 195bb535300SJeff Roberson { 19602c3c858SDavid Xu _pthread_exit_mask(status, NULL); 19702c3c858SDavid Xu } 19802c3c858SDavid Xu 19902c3c858SDavid Xu void 20002c3c858SDavid Xu _pthread_exit_mask(void *status, sigset_t *mask) 20102c3c858SDavid Xu { 202a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 2034cd18a22SMike Makonnen 204bb535300SJeff Roberson /* Check if this thread is already in the process of exiting: */ 205f08e1bf6SDavid Xu if (curthread->cancelling) { 206bb535300SJeff Roberson char msg[128]; 207a091d823SDavid Xu snprintf(msg, sizeof(msg), "Thread %p has called " 208a091d823SDavid Xu "pthread_exit() from a destructor. POSIX 1003.1 " 209a091d823SDavid Xu "1996 s16.2.5.2 does not allow this!", curthread); 210bb535300SJeff Roberson PANIC(msg); 211bb535300SJeff Roberson } 212bb535300SJeff Roberson 213a091d823SDavid Xu /* Flag this thread as exiting. */ 214f08e1bf6SDavid Xu curthread->cancelling = 1; 215cdcffc3fSDavid Xu curthread->cancel_enable = 0; 216cdcffc3fSDavid Xu curthread->cancel_async = 0; 21702c3c858SDavid Xu curthread->cancel_point = 0; 21802c3c858SDavid Xu if (mask != NULL) 21902c3c858SDavid Xu __sys_sigprocmask(SIG_SETMASK, mask, NULL); 22002c3c858SDavid Xu if (curthread->unblock_sigcancel) { 22102c3c858SDavid Xu sigset_t set; 22202c3c858SDavid Xu 22302c3c858SDavid Xu curthread->unblock_sigcancel = 0; 22402c3c858SDavid Xu SIGEMPTYSET(set); 22502c3c858SDavid Xu SIGADDSET(set, SIGCANCEL); 22602c3c858SDavid Xu __sys_sigprocmask(SIG_UNBLOCK, mask, NULL); 22702c3c858SDavid Xu } 228a091d823SDavid Xu 229bb535300SJeff Roberson /* Save the return value: */ 230bb535300SJeff Roberson curthread->ret = status; 2313832fd24SDavid Xu #ifdef _PTHREAD_FORCED_UNWIND 2323832fd24SDavid Xu #ifdef PIC 2333832fd24SDavid Xu thread_uw_init(); 2343832fd24SDavid Xu if (uwl_forcedunwind != NULL) { 2353832fd24SDavid Xu thread_unwind(); 236bb535300SJeff Roberson } 2373832fd24SDavid Xu #else 2383832fd24SDavid Xu if (_Unwind_ForcedUnwind != NULL) { 2393832fd24SDavid Xu thread_unwind(); 2403832fd24SDavid Xu } 2413832fd24SDavid Xu #endif /* PIC */ 2423832fd24SDavid Xu 2433832fd24SDavid Xu else { 2443832fd24SDavid Xu while (curthread->cleanup != NULL) { 2453832fd24SDavid Xu __pthread_cleanup_pop_imp(1); 2463832fd24SDavid Xu } 2473832fd24SDavid Xu exit_thread(); 2483832fd24SDavid Xu } 2493832fd24SDavid Xu 2503832fd24SDavid Xu #else 2513832fd24SDavid Xu while (curthread->cleanup != NULL) { 2523832fd24SDavid Xu __pthread_cleanup_pop_imp(1); 2533832fd24SDavid Xu } 2543832fd24SDavid Xu 2553832fd24SDavid Xu exit_thread(); 2563832fd24SDavid Xu #endif /* _PTHREAD_FORCED_UNWIND */ 2573832fd24SDavid Xu } 2583832fd24SDavid Xu 2593832fd24SDavid Xu static void 2603832fd24SDavid Xu exit_thread(void) 2613832fd24SDavid Xu { 2623832fd24SDavid Xu struct pthread *curthread = _get_curthread(); 26337a6356bSDavid Xu 264bb535300SJeff Roberson /* Check if there is thread specific data: */ 265bb535300SJeff Roberson if (curthread->specific != NULL) { 266bb535300SJeff Roberson /* Run the thread-specific data destructors: */ 267bb535300SJeff Roberson _thread_cleanupspecific(); 268bb535300SJeff Roberson } 269bb535300SJeff Roberson 270a091d823SDavid Xu if (!_thr_isthreaded()) 2714e3f7b6eSMike Makonnen exit(0); 272f97591bfSMike Makonnen 273a9b764e2SDavid Xu if (atomic_fetchadd_int(&_thread_active_threads, -1) == 1) { 274a091d823SDavid Xu exit(0); 275a091d823SDavid Xu /* Never reach! */ 276bb535300SJeff Roberson } 2775b3842aeSJason Evans 2785b3842aeSJason Evans /* Tell malloc that the thread is exiting. */ 2795b3842aeSJason Evans _malloc_thread_cleanup(); 2805b3842aeSJason Evans 281bc414752SDavid Xu THR_LOCK(curthread); 282bc414752SDavid Xu curthread->state = PS_DEAD; 2832ea1f90aSDavid Xu if (curthread->flags & THR_FLAGS_NEED_SUSPEND) { 2842ea1f90aSDavid Xu curthread->cycle++; 2858d6a11a0SDavid Xu _thr_umtx_wake(&curthread->cycle, INT_MAX, 0); 2862ea1f90aSDavid Xu } 287bc414752SDavid Xu /* 288bc414752SDavid Xu * Thread was created with initial refcount 1, we drop the 289bc414752SDavid Xu * reference count to allow it to be garbage collected. 290bc414752SDavid Xu */ 291bc414752SDavid Xu curthread->refcount--; 292a9b764e2SDavid Xu _thr_try_gc(curthread, curthread); /* thread lock released */ 293a9b764e2SDavid Xu 294697b4b49SDavid Xu if (!curthread->force_exit && SHOULD_REPORT_EVENT(curthread, TD_DEATH)) 295d245d9e1SDavid Xu _thr_report_death(curthread); 296d7f119abSDavid Xu 297a9b764e2SDavid Xu #if defined(_PTHREADS_INVARIANTS) 298a9b764e2SDavid Xu if (THR_IN_CRITICAL(curthread)) 299a9b764e2SDavid Xu PANIC("thread exits with resources held!"); 300a9b764e2SDavid Xu #endif 301d7f119abSDavid Xu /* 302d7f119abSDavid Xu * Kernel will do wakeup at the address, so joiner thread 303d7f119abSDavid Xu * will be resumed if it is sleeping at the address. 304d7f119abSDavid Xu */ 305d245d9e1SDavid Xu thr_exit(&curthread->tid); 306a091d823SDavid Xu PANIC("thr_exit() returned"); 307a091d823SDavid Xu /* Never reach! */ 3081c6f6301SMike Makonnen } 309