thr_exit.c revision 212837
1177391Sobrien/*
2107484Speter * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>
381404Speter * All rights reserved.
4175261Sobrien *
5177391Sobrien * Redistribution and use in source and binary forms, with or without
681404Speter * modification, are permitted provided that the following conditions
781404Speter * are met:
881404Speter * 1. Redistributions of source code must retain the above copyright
981404Speter *    notice, this list of conditions and the following disclaimer.
1081404Speter * 2. Redistributions in binary form must reproduce the above copyright
1181404Speter *    notice, this list of conditions and the following disclaimer in the
1281404Speter *    documentation and/or other materials provided with the distribution.
1381404Speter * 3. Neither the name of the author nor the names of any co-contributors
1481404Speter *    may be used to endorse or promote products derived from this software
1581404Speter *    without specific prior written permission.
1681404Speter *
1717721Speter * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
1817721Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19175261Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20175261Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21175261Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22175261Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23175261Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2417721Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2517721Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2617721Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2717721Speter * SUCH DAMAGE.
2817721Speter *
2917721Speter * $FreeBSD: head/lib/libthr/thread/thr_exit.c 212837 2010-09-19 05:19:47Z davidxu $
3017721Speter */
3117721Speter
3217721Speter#include "namespace.h"
3317721Speter#include <errno.h>
3481404Speter#ifdef _PTHREAD_FORCED_UNWIND
35175261Sobrien#include <dlfcn.h>
3681404Speter#endif
3781404Speter#include <stdio.h>
3881404Speter#include <stdlib.h>
3981404Speter#include <pthread.h>
40107484Speter#include <sys/types.h>
41107484Speter#include <sys/signalvar.h>
42107484Speter#include "un-namespace.h"
43107484Speter
4481404Speter#include "libc_private.h"
45128266Speter#include "thr_private.h"
4681404Speter
4781404Spetervoid	_pthread_exit(void *status);
4881404Speter
4981404Speterstatic void	exit_thread(void) __dead2;
5081404Speter
5181404Speter__weak_reference(_pthread_exit, pthread_exit);
52175261Sobrien
53175261Sobrien#ifdef _PTHREAD_FORCED_UNWIND
54175261Sobrien
55175261Sobrienstatic void thread_unwind(void) __dead2;
56175261Sobrien#ifdef PIC
57175261Sobrienstatic void thread_uw_init(void);
58175261Sobrienstatic _Unwind_Reason_Code thread_unwind_stop(int version,
59175261Sobrien	_Unwind_Action actions,
60175261Sobrien	_Unwind_Exception_Class exc_class,
61175261Sobrien	struct _Unwind_Exception *exc_obj,
62175261Sobrien	struct _Unwind_Context *context, void *stop_parameter);
63175261Sobrien/* unwind library pointers */
64175261Sobrienstatic _Unwind_Reason_Code (*uwl_forcedunwind)(struct _Unwind_Exception *,
65175261Sobrien	_Unwind_Stop_Fn, void *);
66175261Sobrienstatic void (*uwl_resume)(struct _Unwind_Exception *exc);
67175261Sobrienstatic _Unwind_Word (*uwl_getcfa)(struct _Unwind_Context *);
68175261Sobrien
69175261Sobrienstatic void
70175261Sobrienthread_uw_init(void)
71175261Sobrien{
72175261Sobrien	static int inited = 0;
73175261Sobrien	Dl_info dlinfo;
74175261Sobrien	void *handle;
75175261Sobrien	void *forcedunwind, *resume, *getcfa;
76175261Sobrien
77175261Sobrien	if (inited)
78175261Sobrien	    return;
79175261Sobrien	handle = RTLD_DEFAULT;
80175261Sobrien	if ((forcedunwind = dlsym(handle, "_Unwind_ForcedUnwind")) != NULL) {
81175261Sobrien	    if (dladdr(forcedunwind, &dlinfo)) {
82128266Speter		if ((handle = dlopen(dlinfo.dli_fname, RTLD_LAZY)) != NULL) {
8381404Speter		    forcedunwind = dlsym(handle, "_Unwind_ForcedUnwind");
84128266Speter		    resume = dlsym(handle, "_Unwind_Resume");
85128266Speter		    getcfa = dlsym(handle, "_Unwind_GetCFA");
86128266Speter		    if (forcedunwind != NULL && resume != NULL &&
8781404Speter			getcfa != NULL) {
8881404Speter			uwl_forcedunwind = forcedunwind;
89128266Speter			uwl_resume = resume;
90128266Speter			uwl_getcfa = getcfa;
91128266Speter		    } else {
92128266Speter			dlclose(handle);
9381404Speter		    }
94128266Speter		}
95128266Speter	    }
9681404Speter	}
97128266Speter	inited = 1;
98128266Speter}
99128266Speter
100107484Spetervoid
101128266Speter_Unwind_Resume(struct _Unwind_Exception *ex)
102128266Speter{
103177391Sobrien	(*uwl_resume)(ex);
104177391Sobrien}
105128266Speter
106128266Speter_Unwind_Reason_Code
107128266Speter_Unwind_ForcedUnwind(struct _Unwind_Exception *ex, _Unwind_Stop_Fn stop_func,
10881404Speter	void *stop_arg)
10981404Speter{
110128266Speter	return (*uwl_forcedunwind)(ex, stop_func, stop_arg);
111128266Speter}
112128266Speter
11381404Speter_Unwind_Word
114128266Speter_Unwind_GetCFA(struct _Unwind_Context *context)
115128266Speter{
116128266Speter	return (*uwl_getcfa)(context);
117177391Sobrien}
118128266Speter#else
119128266Speter#pragma weak _Unwind_GetCFA
12081404Speter#pragma weak _Unwind_ForcedUnwind
121128266Speter#pragma weak _Unwind_Resume
122128266Speter#endif /* PIC */
123128266Speter
124128266Speterstatic void
125128266Speterthread_unwind_cleanup(_Unwind_Reason_Code code, struct _Unwind_Exception *e)
126128266Speter{
12781404Speter	/*
12881404Speter	 * Specification said that _Unwind_Resume should not be used here,
12981404Speter	 * instead, user should rethrow the exception. For C++ user, they
13081404Speter	 * should put "throw" sentence in catch(...) block.
13181404Speter	 */
132128266Speter	PANIC("exception should be rethrown");
133128266Speter}
134128266Speter
135107484Speterstatic _Unwind_Reason_Code
13681404Speterthread_unwind_stop(int version, _Unwind_Action actions,
13781404Speter	_Unwind_Exception_Class exc_class,
13881404Speter	struct _Unwind_Exception *exc_obj,
139177391Sobrien	struct _Unwind_Context *context, void *stop_parameter)
140177391Sobrien{
141177391Sobrien	struct pthread *curthread = _get_curthread();
142177391Sobrien	struct pthread_cleanup *cur;
143177391Sobrien	uintptr_t cfa;
144128266Speter	int done = 0;
145128266Speter
146102840Speter	/* XXX assume stack grows down to lower address */
147128266Speter
148102840Speter	cfa = _Unwind_GetCFA(context);
149175261Sobrien	if (actions & _UA_END_OF_STACK) {
150175261Sobrien		done = 1;
151128266Speter	} else if (cfa >= (uintptr_t)curthread->unwind_stackend) {
152128266Speter		done = 1;
153177391Sobrien	}
154128266Speter
155177391Sobrien	while ((cur = curthread->cleanup) != NULL &&
156177391Sobrien	       (done ||
157177391Sobrien		((uintptr_t)cur < (uintptr_t)curthread->unwind_stackend &&
158128266Speter		 (uintptr_t)cur >= cfa))) {
159128266Speter			__pthread_cleanup_pop_imp(1);
160177391Sobrien	}
161128266Speter
16281404Speter	if (done)
163128266Speter		exit_thread(); /* Never return! */
16481404Speter
165128266Speter	return (_URC_NO_REASON);
166128266Speter}
167177391Sobrien
168128266Speterstatic void
169128266Speterthread_unwind(void)
170175261Sobrien{
171128266Speter	struct pthread  *curthread = _get_curthread();
172177391Sobrien
173128266Speter	curthread->ex.exception_class = 0;
174128266Speter	curthread->ex.exception_cleanup = thread_unwind_cleanup;
175177391Sobrien	_Unwind_ForcedUnwind(&curthread->ex, thread_unwind_stop, NULL);
176128266Speter	PANIC("_Unwind_ForcedUnwind returned");
177128266Speter}
178177391Sobrien
179128266Speter#endif
180128266Speter
181177391Sobrienvoid
182177391Sobrien_thread_exit(const char *fname, int lineno, const char *msg)
183175261Sobrien{
184177391Sobrien
18581404Speter	/* Write an error message to the standard error file descriptor: */
18681404Speter	_thread_printf(2,
187107484Speter	    "Fatal error '%s' at line %d in file %s (errno = %d)\n",
18881404Speter	    msg, lineno, fname, errno);
18981404Speter
19081404Speter	abort();
19181404Speter}
19281404Speter
19381404Spetervoid
19481404Speter_pthread_exit(void *status)
19581404Speter{
19681404Speter	_pthread_exit_mask(status, NULL);
19781404Speter}
198102840Speter
19981404Spetervoid
20081404Speter_pthread_exit_mask(void *status, sigset_t *mask)
20181404Speter{
20281404Speter	struct pthread *curthread = _get_curthread();
20317721Speter
20481404Speter	/* Check if this thread is already in the process of exiting: */
20581404Speter	if (curthread->cancelling) {
20681404Speter		char msg[128];
20717721Speter		snprintf(msg, sizeof(msg), "Thread %p has called "
20881404Speter		    "pthread_exit() from a destructor. POSIX 1003.1 "
20981404Speter		    "1996 s16.2.5.2 does not allow this!", curthread);
21017721Speter		PANIC(msg);
21181404Speter	}
21281404Speter
21381404Speter	/* Flag this thread as exiting. */
21481404Speter	curthread->cancelling = 1;
21581404Speter	curthread->cancel_enable = 0;
21681404Speter	curthread->cancel_async = 0;
21781404Speter	curthread->cancel_point = 0;
21881404Speter	if (mask != NULL)
21981404Speter		__sys_sigprocmask(SIG_SETMASK, mask, NULL);
22081404Speter	if (curthread->unblock_sigcancel) {
221175261Sobrien		sigset_t set;
22281404Speter
22381404Speter		curthread->unblock_sigcancel = 0;
22481404Speter		SIGEMPTYSET(set);
22581404Speter		SIGADDSET(set, SIGCANCEL);
22681404Speter		__sys_sigprocmask(SIG_UNBLOCK, mask, NULL);
22781404Speter	}
22881404Speter
22981404Speter	/* Save the return value: */
23081404Speter	curthread->ret = status;
23181404Speter#ifdef _PTHREAD_FORCED_UNWIND
23281404Speter#ifdef PIC
23381404Speter	thread_uw_init();
23481404Speter	if (uwl_forcedunwind != NULL) {
23581404Speter		thread_unwind();
236175261Sobrien	}
237175261Sobrien#else
238175261Sobrien	if (_Unwind_ForcedUnwind != NULL) {
239175261Sobrien		thread_unwind();
240175261Sobrien	}
241175261Sobrien#endif /* PIC */
242175261Sobrien
243175261Sobrien 	else {
244175261Sobrien		while (curthread->cleanup != NULL) {
245175261Sobrien			__pthread_cleanup_pop_imp(1);
24681404Speter		}
24781404Speter		exit_thread();
248175261Sobrien	}
249175261Sobrien
250175261Sobrien#else
251175261Sobrien	while (curthread->cleanup != NULL) {
252175261Sobrien		__pthread_cleanup_pop_imp(1);
253175261Sobrien	}
254175261Sobrien
255175261Sobrien	exit_thread();
256175261Sobrien#endif /* _PTHREAD_FORCED_UNWIND */
257175261Sobrien}
258175261Sobrien
259175261Sobrienstatic void
260175261Sobrienexit_thread(void)
261175261Sobrien{
262175261Sobrien	struct pthread *curthread = _get_curthread();
263175261Sobrien
264175261Sobrien	/* Check if there is thread specific data: */
265175261Sobrien	if (curthread->specific != NULL) {
266107484Speter		/* Run the thread-specific data destructors: */
267175261Sobrien		_thread_cleanupspecific();
268107484Speter	}
269175261Sobrien
270107484Speter	if (!_thr_isthreaded())
271175261Sobrien		exit(0);
272107484Speter
273175261Sobrien	if (atomic_fetchadd_int(&_thread_active_threads, -1) == 1) {
274107484Speter		exit(0);
275175261Sobrien		/* Never reach! */
276107484Speter	}
277175261Sobrien
278107484Speter	/* Tell malloc that the thread is exiting. */
279175261Sobrien	_malloc_thread_cleanup();
280107484Speter
281175261Sobrien	THR_LOCK(curthread);
282107484Speter	curthread->state = PS_DEAD;
283175261Sobrien	if (curthread->flags & THR_FLAGS_NEED_SUSPEND) {
284128266Speter		curthread->cycle++;
285175261Sobrien		_thr_umtx_wake(&curthread->cycle, INT_MAX, 0);
286107484Speter	}
287175261Sobrien	/*
288107484Speter	 * Thread was created with initial refcount 1, we drop the
28981404Speter	 * reference count to allow it to be garbage collected.
29081404Speter	 */
291177391Sobrien	curthread->refcount--;
29281404Speter	_thr_try_gc(curthread, curthread); /* thread lock released */
293107484Speter
294107484Speter	if (!curthread->force_exit && SHOULD_REPORT_EVENT(curthread, TD_DEATH))
295107484Speter		_thr_report_death(curthread);
296175261Sobrien
297175261Sobrien#if defined(_PTHREADS_INVARIANTS)
29881404Speter	if (THR_IN_CRITICAL(curthread))
29917721Speter		PANIC("thread exits with resources held!");
30081404Speter#endif
30181404Speter	/*
30281404Speter	 * Kernel will do wakeup at the address, so joiner thread
30381404Speter	 * will be resumed if it is sleeping at the address.
304107484Speter	 */
305175261Sobrien	thr_exit(&curthread->tid);
306175261Sobrien	PANIC("thr_exit() returned");
30717721Speter	/* Never reach! */
30881404Speter}
30981404Speter