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 _Unwind_Word (*uwl_getcfa)(struct _Unwind_Context *); 673832fd24SDavid Xu 683832fd24SDavid Xu static void 693832fd24SDavid Xu thread_uw_init(void) 703832fd24SDavid Xu { 713832fd24SDavid Xu static int inited = 0; 724da1da4bSDavid Xu Dl_info dlinfo; 733832fd24SDavid Xu void *handle; 74a5793db9SDavid Xu void *forcedunwind, *getcfa; 753832fd24SDavid Xu 763832fd24SDavid Xu if (inited) 773832fd24SDavid Xu return; 783832fd24SDavid Xu handle = RTLD_DEFAULT; 794da1da4bSDavid Xu if ((forcedunwind = dlsym(handle, "_Unwind_ForcedUnwind")) != NULL) { 804da1da4bSDavid Xu if (dladdr(forcedunwind, &dlinfo)) { 81a5793db9SDavid Xu /* 82a5793db9SDavid Xu * Make sure the address is always valid by holding the library, 83a5793db9SDavid Xu * also assume functions are in same library. 84a5793db9SDavid Xu */ 854da1da4bSDavid Xu if ((handle = dlopen(dlinfo.dli_fname, RTLD_LAZY)) != NULL) { 864da1da4bSDavid Xu forcedunwind = dlsym(handle, "_Unwind_ForcedUnwind"); 874da1da4bSDavid Xu getcfa = dlsym(handle, "_Unwind_GetCFA"); 88a5793db9SDavid Xu if (forcedunwind != NULL && getcfa != NULL) { 894da1da4bSDavid Xu uwl_getcfa = getcfa; 90a5793db9SDavid Xu atomic_store_rel_ptr((volatile void *)&uwl_forcedunwind, 91a5793db9SDavid Xu (uintptr_t)forcedunwind); 924da1da4bSDavid Xu } else { 934da1da4bSDavid Xu dlclose(handle); 943832fd24SDavid Xu } 953832fd24SDavid Xu } 964da1da4bSDavid Xu } 974da1da4bSDavid Xu } 984da1da4bSDavid Xu inited = 1; 994da1da4bSDavid Xu } 1003832fd24SDavid Xu 1013832fd24SDavid Xu _Unwind_Reason_Code 1023832fd24SDavid Xu _Unwind_ForcedUnwind(struct _Unwind_Exception *ex, _Unwind_Stop_Fn stop_func, 1033832fd24SDavid Xu void *stop_arg) 1043832fd24SDavid Xu { 1053832fd24SDavid Xu return (*uwl_forcedunwind)(ex, stop_func, stop_arg); 1063832fd24SDavid Xu } 1073832fd24SDavid Xu 1083832fd24SDavid Xu _Unwind_Word 1093832fd24SDavid Xu _Unwind_GetCFA(struct _Unwind_Context *context) 1103832fd24SDavid Xu { 1113832fd24SDavid Xu return (*uwl_getcfa)(context); 1123832fd24SDavid Xu } 1133832fd24SDavid Xu #else 1143832fd24SDavid Xu #pragma weak _Unwind_GetCFA 1153832fd24SDavid Xu #pragma weak _Unwind_ForcedUnwind 1163832fd24SDavid Xu #endif /* PIC */ 1173832fd24SDavid Xu 1183832fd24SDavid Xu static void 1193832fd24SDavid Xu thread_unwind_cleanup(_Unwind_Reason_Code code, struct _Unwind_Exception *e) 1203832fd24SDavid Xu { 1213832fd24SDavid Xu /* 1223832fd24SDavid Xu * Specification said that _Unwind_Resume should not be used here, 1233832fd24SDavid Xu * instead, user should rethrow the exception. For C++ user, they 1243832fd24SDavid Xu * should put "throw" sentence in catch(...) block. 1253832fd24SDavid Xu */ 1263832fd24SDavid Xu PANIC("exception should be rethrown"); 1273832fd24SDavid Xu } 1283832fd24SDavid Xu 1293832fd24SDavid Xu static _Unwind_Reason_Code 1303832fd24SDavid Xu thread_unwind_stop(int version, _Unwind_Action actions, 1313832fd24SDavid Xu _Unwind_Exception_Class exc_class, 1323832fd24SDavid Xu struct _Unwind_Exception *exc_obj, 1333832fd24SDavid Xu struct _Unwind_Context *context, void *stop_parameter) 1343832fd24SDavid Xu { 1353832fd24SDavid Xu struct pthread *curthread = _get_curthread(); 1363832fd24SDavid Xu struct pthread_cleanup *cur; 1373832fd24SDavid Xu uintptr_t cfa; 1383832fd24SDavid Xu int done = 0; 1393832fd24SDavid Xu 1403832fd24SDavid Xu /* XXX assume stack grows down to lower address */ 1413832fd24SDavid Xu 1423832fd24SDavid Xu cfa = _Unwind_GetCFA(context); 143*6f066bb3SDavid Xu if (actions & _UA_END_OF_STACK || 144*6f066bb3SDavid Xu cfa >= (uintptr_t)curthread->unwind_stackend) { 1453832fd24SDavid Xu done = 1; 1463832fd24SDavid Xu } 1473832fd24SDavid Xu 1483832fd24SDavid Xu while ((cur = curthread->cleanup) != NULL && 149*6f066bb3SDavid Xu (done || (uintptr_t)cur <= cfa)) { 1503832fd24SDavid Xu __pthread_cleanup_pop_imp(1); 1513832fd24SDavid Xu } 1523832fd24SDavid Xu 1533832fd24SDavid Xu if (done) 1543832fd24SDavid Xu exit_thread(); /* Never return! */ 1553832fd24SDavid Xu 1563832fd24SDavid Xu return (_URC_NO_REASON); 1573832fd24SDavid Xu } 1583832fd24SDavid Xu 1593832fd24SDavid Xu static void 1603832fd24SDavid Xu thread_unwind(void) 1613832fd24SDavid Xu { 1623832fd24SDavid Xu struct pthread *curthread = _get_curthread(); 1633832fd24SDavid Xu 1643832fd24SDavid Xu curthread->ex.exception_class = 0; 1653832fd24SDavid Xu curthread->ex.exception_cleanup = thread_unwind_cleanup; 1663832fd24SDavid Xu _Unwind_ForcedUnwind(&curthread->ex, thread_unwind_stop, NULL); 1673832fd24SDavid Xu PANIC("_Unwind_ForcedUnwind returned"); 1683832fd24SDavid Xu } 1693832fd24SDavid Xu 1703832fd24SDavid Xu #endif 1713832fd24SDavid Xu 172bb535300SJeff Roberson void 17337a6356bSDavid Xu _thread_exit(const char *fname, int lineno, const char *msg) 174bb535300SJeff Roberson { 175bb535300SJeff Roberson 176a091d823SDavid Xu /* Write an error message to the standard error file descriptor: */ 177a091d823SDavid Xu _thread_printf(2, 178bb535300SJeff Roberson "Fatal error '%s' at line %d in file %s (errno = %d)\n", 179a091d823SDavid Xu msg, lineno, fname, errno); 180bb535300SJeff Roberson 181bb535300SJeff Roberson abort(); 182bb535300SJeff Roberson } 183bb535300SJeff Roberson 184bb535300SJeff Roberson void 185bb535300SJeff Roberson _pthread_exit(void *status) 186bb535300SJeff Roberson { 18702c3c858SDavid Xu _pthread_exit_mask(status, NULL); 18802c3c858SDavid Xu } 18902c3c858SDavid Xu 19002c3c858SDavid Xu void 19102c3c858SDavid Xu _pthread_exit_mask(void *status, sigset_t *mask) 19202c3c858SDavid Xu { 193a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 1944cd18a22SMike Makonnen 195bb535300SJeff Roberson /* Check if this thread is already in the process of exiting: */ 196f08e1bf6SDavid Xu if (curthread->cancelling) { 197bb535300SJeff Roberson char msg[128]; 198a091d823SDavid Xu snprintf(msg, sizeof(msg), "Thread %p has called " 199a091d823SDavid Xu "pthread_exit() from a destructor. POSIX 1003.1 " 200a091d823SDavid Xu "1996 s16.2.5.2 does not allow this!", curthread); 201bb535300SJeff Roberson PANIC(msg); 202bb535300SJeff Roberson } 203bb535300SJeff Roberson 204a091d823SDavid Xu /* Flag this thread as exiting. */ 205f08e1bf6SDavid Xu curthread->cancelling = 1; 2064173ebefSDavid Xu curthread->no_cancel = 1; 207cdcffc3fSDavid Xu curthread->cancel_async = 0; 20802c3c858SDavid Xu curthread->cancel_point = 0; 20902c3c858SDavid Xu if (mask != NULL) 21002c3c858SDavid Xu __sys_sigprocmask(SIG_SETMASK, mask, NULL); 21102c3c858SDavid Xu if (curthread->unblock_sigcancel) { 21202c3c858SDavid Xu sigset_t set; 21302c3c858SDavid Xu 21402c3c858SDavid Xu curthread->unblock_sigcancel = 0; 21502c3c858SDavid Xu SIGEMPTYSET(set); 21602c3c858SDavid Xu SIGADDSET(set, SIGCANCEL); 21702c3c858SDavid Xu __sys_sigprocmask(SIG_UNBLOCK, mask, NULL); 21802c3c858SDavid Xu } 219a091d823SDavid Xu 220bb535300SJeff Roberson /* Save the return value: */ 221bb535300SJeff Roberson curthread->ret = status; 2223832fd24SDavid Xu #ifdef _PTHREAD_FORCED_UNWIND 2233832fd24SDavid Xu #ifdef PIC 2243832fd24SDavid Xu thread_uw_init(); 2253832fd24SDavid Xu if (uwl_forcedunwind != NULL) { 2263832fd24SDavid Xu thread_unwind(); 227bb535300SJeff Roberson } 2283832fd24SDavid Xu #else 2293832fd24SDavid Xu if (_Unwind_ForcedUnwind != NULL) { 2303832fd24SDavid Xu thread_unwind(); 2313832fd24SDavid Xu } 2323832fd24SDavid Xu #endif /* PIC */ 2333832fd24SDavid Xu 2343832fd24SDavid Xu else { 2353832fd24SDavid Xu while (curthread->cleanup != NULL) { 2363832fd24SDavid Xu __pthread_cleanup_pop_imp(1); 2373832fd24SDavid Xu } 2383832fd24SDavid Xu exit_thread(); 2393832fd24SDavid Xu } 2403832fd24SDavid Xu 2413832fd24SDavid Xu #else 2423832fd24SDavid Xu while (curthread->cleanup != NULL) { 2433832fd24SDavid Xu __pthread_cleanup_pop_imp(1); 2443832fd24SDavid Xu } 2453832fd24SDavid Xu 2463832fd24SDavid Xu exit_thread(); 2473832fd24SDavid Xu #endif /* _PTHREAD_FORCED_UNWIND */ 2483832fd24SDavid Xu } 2493832fd24SDavid Xu 2503832fd24SDavid Xu static void 2513832fd24SDavid Xu exit_thread(void) 2523832fd24SDavid Xu { 2533832fd24SDavid Xu struct pthread *curthread = _get_curthread(); 25437a6356bSDavid Xu 255bb535300SJeff Roberson /* Check if there is thread specific data: */ 256bb535300SJeff Roberson if (curthread->specific != NULL) { 257bb535300SJeff Roberson /* Run the thread-specific data destructors: */ 258bb535300SJeff Roberson _thread_cleanupspecific(); 259bb535300SJeff Roberson } 260bb535300SJeff Roberson 261a091d823SDavid Xu if (!_thr_isthreaded()) 2624e3f7b6eSMike Makonnen exit(0); 263f97591bfSMike Makonnen 264a9b764e2SDavid Xu if (atomic_fetchadd_int(&_thread_active_threads, -1) == 1) { 265a091d823SDavid Xu exit(0); 266a091d823SDavid Xu /* Never reach! */ 267bb535300SJeff Roberson } 2685b3842aeSJason Evans 2695b3842aeSJason Evans /* Tell malloc that the thread is exiting. */ 2705b3842aeSJason Evans _malloc_thread_cleanup(); 2715b3842aeSJason Evans 272bc414752SDavid Xu THR_LOCK(curthread); 273bc414752SDavid Xu curthread->state = PS_DEAD; 2742ea1f90aSDavid Xu if (curthread->flags & THR_FLAGS_NEED_SUSPEND) { 2752ea1f90aSDavid Xu curthread->cycle++; 2768d6a11a0SDavid Xu _thr_umtx_wake(&curthread->cycle, INT_MAX, 0); 2772ea1f90aSDavid Xu } 278bc414752SDavid Xu /* 279bc414752SDavid Xu * Thread was created with initial refcount 1, we drop the 280bc414752SDavid Xu * reference count to allow it to be garbage collected. 281bc414752SDavid Xu */ 282bc414752SDavid Xu curthread->refcount--; 283a9b764e2SDavid Xu _thr_try_gc(curthread, curthread); /* thread lock released */ 284a9b764e2SDavid Xu 285697b4b49SDavid Xu if (!curthread->force_exit && SHOULD_REPORT_EVENT(curthread, TD_DEATH)) 286d245d9e1SDavid Xu _thr_report_death(curthread); 287d7f119abSDavid Xu 288a9b764e2SDavid Xu #if defined(_PTHREADS_INVARIANTS) 289a9b764e2SDavid Xu if (THR_IN_CRITICAL(curthread)) 290a9b764e2SDavid Xu PANIC("thread exits with resources held!"); 291a9b764e2SDavid Xu #endif 292d7f119abSDavid Xu /* 293d7f119abSDavid Xu * Kernel will do wakeup at the address, so joiner thread 294d7f119abSDavid Xu * will be resumed if it is sleeping at the address. 295d7f119abSDavid Xu */ 296d245d9e1SDavid Xu thr_exit(&curthread->tid); 297a091d823SDavid Xu PANIC("thr_exit() returned"); 298a091d823SDavid Xu /* Never reach! */ 2991c6f6301SMike Makonnen } 300