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 54*8690b9f6SDavid Xu static int message_printed; 553832fd24SDavid Xu 563832fd24SDavid Xu static void thread_unwind(void) __dead2; 573832fd24SDavid Xu #ifdef PIC 583832fd24SDavid Xu static void thread_uw_init(void); 593832fd24SDavid Xu static _Unwind_Reason_Code thread_unwind_stop(int version, 603832fd24SDavid Xu _Unwind_Action actions, 613832fd24SDavid Xu _Unwind_Exception_Class exc_class, 623832fd24SDavid Xu struct _Unwind_Exception *exc_obj, 633832fd24SDavid Xu struct _Unwind_Context *context, void *stop_parameter); 643832fd24SDavid Xu /* unwind library pointers */ 653832fd24SDavid Xu static _Unwind_Reason_Code (*uwl_forcedunwind)(struct _Unwind_Exception *, 663832fd24SDavid Xu _Unwind_Stop_Fn, void *); 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; 734da1da4bSDavid Xu Dl_info dlinfo; 743832fd24SDavid Xu void *handle; 75a5793db9SDavid Xu void *forcedunwind, *getcfa; 763832fd24SDavid Xu 773832fd24SDavid Xu if (inited) 783832fd24SDavid Xu return; 793832fd24SDavid Xu handle = RTLD_DEFAULT; 804da1da4bSDavid Xu if ((forcedunwind = dlsym(handle, "_Unwind_ForcedUnwind")) != NULL) { 814da1da4bSDavid Xu if (dladdr(forcedunwind, &dlinfo)) { 82a5793db9SDavid Xu /* 83a5793db9SDavid Xu * Make sure the address is always valid by holding the library, 84a5793db9SDavid Xu * also assume functions are in same library. 85a5793db9SDavid Xu */ 864da1da4bSDavid Xu if ((handle = dlopen(dlinfo.dli_fname, RTLD_LAZY)) != NULL) { 874da1da4bSDavid Xu forcedunwind = dlsym(handle, "_Unwind_ForcedUnwind"); 884da1da4bSDavid Xu getcfa = dlsym(handle, "_Unwind_GetCFA"); 89a5793db9SDavid Xu if (forcedunwind != NULL && getcfa != NULL) { 904da1da4bSDavid Xu uwl_getcfa = getcfa; 91a5793db9SDavid Xu atomic_store_rel_ptr((volatile void *)&uwl_forcedunwind, 92a5793db9SDavid Xu (uintptr_t)forcedunwind); 934da1da4bSDavid Xu } else { 944da1da4bSDavid Xu dlclose(handle); 953832fd24SDavid Xu } 963832fd24SDavid Xu } 974da1da4bSDavid Xu } 984da1da4bSDavid Xu } 994da1da4bSDavid Xu inited = 1; 1004da1da4bSDavid Xu } 1013832fd24SDavid Xu 1023832fd24SDavid Xu _Unwind_Reason_Code 1033832fd24SDavid Xu _Unwind_ForcedUnwind(struct _Unwind_Exception *ex, _Unwind_Stop_Fn stop_func, 1043832fd24SDavid Xu void *stop_arg) 1053832fd24SDavid Xu { 1063832fd24SDavid Xu return (*uwl_forcedunwind)(ex, stop_func, stop_arg); 1073832fd24SDavid Xu } 1083832fd24SDavid Xu 1093832fd24SDavid Xu _Unwind_Word 1103832fd24SDavid Xu _Unwind_GetCFA(struct _Unwind_Context *context) 1113832fd24SDavid Xu { 1123832fd24SDavid Xu return (*uwl_getcfa)(context); 1133832fd24SDavid Xu } 1143832fd24SDavid Xu #else 1153832fd24SDavid Xu #pragma weak _Unwind_GetCFA 1163832fd24SDavid Xu #pragma weak _Unwind_ForcedUnwind 1173832fd24SDavid Xu #endif /* PIC */ 1183832fd24SDavid Xu 1193832fd24SDavid Xu static void 1203832fd24SDavid Xu thread_unwind_cleanup(_Unwind_Reason_Code code, struct _Unwind_Exception *e) 1213832fd24SDavid Xu { 1223832fd24SDavid Xu /* 1233832fd24SDavid Xu * Specification said that _Unwind_Resume should not be used here, 1243832fd24SDavid Xu * instead, user should rethrow the exception. For C++ user, they 1253832fd24SDavid Xu * should put "throw" sentence in catch(...) block. 1263832fd24SDavid Xu */ 1273832fd24SDavid Xu PANIC("exception should be rethrown"); 1283832fd24SDavid Xu } 1293832fd24SDavid Xu 1303832fd24SDavid Xu static _Unwind_Reason_Code 1313832fd24SDavid Xu thread_unwind_stop(int version, _Unwind_Action actions, 1323832fd24SDavid Xu _Unwind_Exception_Class exc_class, 1333832fd24SDavid Xu struct _Unwind_Exception *exc_obj, 1343832fd24SDavid Xu struct _Unwind_Context *context, void *stop_parameter) 1353832fd24SDavid Xu { 1363832fd24SDavid Xu struct pthread *curthread = _get_curthread(); 1373832fd24SDavid Xu struct pthread_cleanup *cur; 1383832fd24SDavid Xu uintptr_t cfa; 1393832fd24SDavid Xu int done = 0; 1403832fd24SDavid Xu 1413832fd24SDavid Xu /* XXX assume stack grows down to lower address */ 1423832fd24SDavid Xu 1433832fd24SDavid Xu cfa = _Unwind_GetCFA(context); 1446f066bb3SDavid Xu if (actions & _UA_END_OF_STACK || 1456f066bb3SDavid Xu cfa >= (uintptr_t)curthread->unwind_stackend) { 1463832fd24SDavid Xu done = 1; 1473832fd24SDavid Xu } 1483832fd24SDavid Xu 1493832fd24SDavid Xu while ((cur = curthread->cleanup) != NULL && 1506f066bb3SDavid Xu (done || (uintptr_t)cur <= cfa)) { 1513832fd24SDavid Xu __pthread_cleanup_pop_imp(1); 1523832fd24SDavid Xu } 1533832fd24SDavid Xu 1543832fd24SDavid Xu if (done) 1553832fd24SDavid Xu exit_thread(); /* Never return! */ 1563832fd24SDavid Xu 1573832fd24SDavid Xu return (_URC_NO_REASON); 1583832fd24SDavid Xu } 1593832fd24SDavid Xu 1603832fd24SDavid Xu static void 1613832fd24SDavid Xu thread_unwind(void) 1623832fd24SDavid Xu { 1633832fd24SDavid Xu struct pthread *curthread = _get_curthread(); 1643832fd24SDavid Xu 1653832fd24SDavid Xu curthread->ex.exception_class = 0; 1663832fd24SDavid Xu curthread->ex.exception_cleanup = thread_unwind_cleanup; 1673832fd24SDavid Xu _Unwind_ForcedUnwind(&curthread->ex, thread_unwind_stop, NULL); 1683832fd24SDavid Xu PANIC("_Unwind_ForcedUnwind returned"); 1693832fd24SDavid Xu } 1703832fd24SDavid Xu 1713832fd24SDavid Xu #endif 1723832fd24SDavid Xu 173bb535300SJeff Roberson void 17437a6356bSDavid Xu _thread_exit(const char *fname, int lineno, const char *msg) 175bb535300SJeff Roberson { 176bb535300SJeff Roberson 177a091d823SDavid Xu /* Write an error message to the standard error file descriptor: */ 178a091d823SDavid Xu _thread_printf(2, 179bb535300SJeff Roberson "Fatal error '%s' at line %d in file %s (errno = %d)\n", 180a091d823SDavid Xu msg, lineno, fname, errno); 181bb535300SJeff Roberson 182bb535300SJeff Roberson abort(); 183bb535300SJeff Roberson } 184bb535300SJeff Roberson 185bb535300SJeff Roberson void 186bb535300SJeff Roberson _pthread_exit(void *status) 187bb535300SJeff Roberson { 18802c3c858SDavid Xu _pthread_exit_mask(status, NULL); 18902c3c858SDavid Xu } 19002c3c858SDavid Xu 19102c3c858SDavid Xu void 19202c3c858SDavid Xu _pthread_exit_mask(void *status, sigset_t *mask) 19302c3c858SDavid Xu { 194a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 1954cd18a22SMike Makonnen 196bb535300SJeff Roberson /* Check if this thread is already in the process of exiting: */ 197f08e1bf6SDavid Xu if (curthread->cancelling) { 198bb535300SJeff Roberson char msg[128]; 199a091d823SDavid Xu snprintf(msg, sizeof(msg), "Thread %p has called " 200a091d823SDavid Xu "pthread_exit() from a destructor. POSIX 1003.1 " 201a091d823SDavid Xu "1996 s16.2.5.2 does not allow this!", curthread); 202bb535300SJeff Roberson PANIC(msg); 203bb535300SJeff Roberson } 204bb535300SJeff Roberson 205a091d823SDavid Xu /* Flag this thread as exiting. */ 206f08e1bf6SDavid Xu curthread->cancelling = 1; 2074173ebefSDavid Xu curthread->no_cancel = 1; 208cdcffc3fSDavid Xu curthread->cancel_async = 0; 20902c3c858SDavid Xu curthread->cancel_point = 0; 21002c3c858SDavid Xu if (mask != NULL) 21102c3c858SDavid Xu __sys_sigprocmask(SIG_SETMASK, mask, NULL); 21202c3c858SDavid Xu if (curthread->unblock_sigcancel) { 21302c3c858SDavid Xu sigset_t set; 21402c3c858SDavid Xu 21502c3c858SDavid Xu curthread->unblock_sigcancel = 0; 21602c3c858SDavid Xu SIGEMPTYSET(set); 21702c3c858SDavid Xu SIGADDSET(set, SIGCANCEL); 21802c3c858SDavid Xu __sys_sigprocmask(SIG_UNBLOCK, mask, NULL); 21902c3c858SDavid Xu } 220a091d823SDavid Xu 221bb535300SJeff Roberson /* Save the return value: */ 222bb535300SJeff Roberson curthread->ret = status; 2233832fd24SDavid Xu #ifdef _PTHREAD_FORCED_UNWIND 224*8690b9f6SDavid Xu 2253832fd24SDavid Xu #ifdef PIC 2263832fd24SDavid Xu thread_uw_init(); 2273832fd24SDavid Xu #endif /* PIC */ 2283832fd24SDavid Xu 229*8690b9f6SDavid Xu #ifdef PIC 230*8690b9f6SDavid Xu if (uwl_forcedunwind != NULL) { 231*8690b9f6SDavid Xu #else 232*8690b9f6SDavid Xu if (_Unwind_ForcedUnwind != NULL) { 233*8690b9f6SDavid Xu #endif 234*8690b9f6SDavid Xu if (curthread->unwind_disabled) { 235*8690b9f6SDavid Xu if (message_printed == 0) { 236*8690b9f6SDavid Xu message_printed = 1; 237*8690b9f6SDavid Xu _thread_printf(2, "Warning: old _pthread_cleanup_push was called, " 238*8690b9f6SDavid Xu "stack unwinding is disabled.\n"); 239*8690b9f6SDavid Xu } 240*8690b9f6SDavid Xu goto cleanup; 241*8690b9f6SDavid Xu } 242*8690b9f6SDavid Xu thread_unwind(); 243*8690b9f6SDavid Xu 244*8690b9f6SDavid Xu } else { 245*8690b9f6SDavid Xu cleanup: 2463832fd24SDavid Xu while (curthread->cleanup != NULL) { 2473832fd24SDavid Xu __pthread_cleanup_pop_imp(1); 2483832fd24SDavid Xu } 2493832fd24SDavid Xu exit_thread(); 2503832fd24SDavid Xu } 2513832fd24SDavid Xu 2523832fd24SDavid Xu #else 2533832fd24SDavid Xu while (curthread->cleanup != NULL) { 2543832fd24SDavid Xu __pthread_cleanup_pop_imp(1); 2553832fd24SDavid Xu } 2563832fd24SDavid Xu 2573832fd24SDavid Xu exit_thread(); 2583832fd24SDavid Xu #endif /* _PTHREAD_FORCED_UNWIND */ 2593832fd24SDavid Xu } 2603832fd24SDavid Xu 2613832fd24SDavid Xu static void 2623832fd24SDavid Xu exit_thread(void) 2633832fd24SDavid Xu { 2643832fd24SDavid Xu struct pthread *curthread = _get_curthread(); 26537a6356bSDavid Xu 266bb535300SJeff Roberson /* Check if there is thread specific data: */ 267bb535300SJeff Roberson if (curthread->specific != NULL) { 268bb535300SJeff Roberson /* Run the thread-specific data destructors: */ 269bb535300SJeff Roberson _thread_cleanupspecific(); 270bb535300SJeff Roberson } 271bb535300SJeff Roberson 272a091d823SDavid Xu if (!_thr_isthreaded()) 2734e3f7b6eSMike Makonnen exit(0); 274f97591bfSMike Makonnen 275a9b764e2SDavid Xu if (atomic_fetchadd_int(&_thread_active_threads, -1) == 1) { 276a091d823SDavid Xu exit(0); 277a091d823SDavid Xu /* Never reach! */ 278bb535300SJeff Roberson } 2795b3842aeSJason Evans 2805b3842aeSJason Evans /* Tell malloc that the thread is exiting. */ 2815b3842aeSJason Evans _malloc_thread_cleanup(); 2825b3842aeSJason Evans 283bc414752SDavid Xu THR_LOCK(curthread); 284bc414752SDavid Xu curthread->state = PS_DEAD; 2852ea1f90aSDavid Xu if (curthread->flags & THR_FLAGS_NEED_SUSPEND) { 2862ea1f90aSDavid Xu curthread->cycle++; 2878d6a11a0SDavid Xu _thr_umtx_wake(&curthread->cycle, INT_MAX, 0); 2882ea1f90aSDavid Xu } 289bc414752SDavid Xu /* 290bc414752SDavid Xu * Thread was created with initial refcount 1, we drop the 291bc414752SDavid Xu * reference count to allow it to be garbage collected. 292bc414752SDavid Xu */ 293bc414752SDavid Xu curthread->refcount--; 294a9b764e2SDavid Xu _thr_try_gc(curthread, curthread); /* thread lock released */ 295a9b764e2SDavid Xu 296697b4b49SDavid Xu if (!curthread->force_exit && SHOULD_REPORT_EVENT(curthread, TD_DEATH)) 297d245d9e1SDavid Xu _thr_report_death(curthread); 298d7f119abSDavid Xu 299a9b764e2SDavid Xu #if defined(_PTHREADS_INVARIANTS) 300a9b764e2SDavid Xu if (THR_IN_CRITICAL(curthread)) 301a9b764e2SDavid Xu PANIC("thread exits with resources held!"); 302a9b764e2SDavid Xu #endif 303d7f119abSDavid Xu /* 304d7f119abSDavid Xu * Kernel will do wakeup at the address, so joiner thread 305d7f119abSDavid Xu * will be resumed if it is sleeping at the address. 306d7f119abSDavid Xu */ 307d245d9e1SDavid Xu thr_exit(&curthread->tid); 308a091d823SDavid Xu PANIC("thr_exit() returned"); 309a091d823SDavid Xu /* Never reach! */ 3101c6f6301SMike Makonnen } 311