1 /* $OpenBSD: uthread_exit.c,v 1.17 2002/11/07 02:56:20 marc Exp $ */
2 /*
3 * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by John Birrell.
17 * 4. Neither the name of the author nor the names of any co-contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * $FreeBSD: uthread_exit.c,v 1.12 1999/08/30 15:45:42 dt Exp $
34 */
35 #include <errno.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <string.h>
39 #include <signal.h>
40 #include <sys/types.h>
41 #ifdef _THREAD_SAFE
42 #include <pthread.h>
43 #include "pthread_private.h"
44
45 void
_exit(int status)46 _exit(int status)
47 {
48 int flags;
49 int i;
50 struct itimerval itimer;
51
52 /* Disable the interval timer: */
53 itimer.it_interval.tv_sec = 0;
54 itimer.it_interval.tv_usec = 0;
55 itimer.it_value.tv_sec = 0;
56 itimer.it_value.tv_usec = 0;
57 setitimer(_ITIMER_SCHED_TIMER, &itimer, NULL);
58
59 /* Close the pthread kernel pipe: */
60 _thread_sys_close(_thread_kern_pipe[0]);
61 _thread_sys_close(_thread_kern_pipe[1]);
62
63 /*
64 * Enter a loop to set all file descriptors to blocking
65 * if they were not created as non-blocking:
66 */
67 for (i = 0; i < _thread_dtablesize; i++) {
68 /* Check if this file descriptor is in use: */
69 if (_thread_fd_table[i] != NULL &&
70 !(_thread_fd_table[i]->flags & O_NONBLOCK)) {
71 /* Get the current flags: */
72 flags = _thread_sys_fcntl(i, F_GETFL, NULL);
73 /* Clear the nonblocking file descriptor flag: */
74 _thread_sys_fcntl(i, F_SETFL, flags & ~O_NONBLOCK);
75 }
76 }
77
78 /* Call the _exit syscall: */
79 _thread_sys__exit(status);
80 }
81
82 static void
numlcat(char * s,int l,size_t sz)83 numlcat(char *s, int l, size_t sz)
84 {
85 char digit[2];
86
87 /* Inefficient. */
88 if (l < 0) {
89 l = -l;
90 strlcat(s, "-", sz);
91 }
92 if (l >= 10)
93 numlcat(s, l / 10, sz);
94 digit[0] = "0123456789"[l % 10];
95 digit[1] = '\0';
96 strlcat(s, digit, sz);
97 }
98
99 void
_thread_exit(const char * fname,int lineno,const char * string)100 _thread_exit(const char *fname, int lineno, const char *string)
101 {
102 char s[256];
103
104 /* Prepare an error message string: */
105 s[0] = '\0';
106 strlcat(s, "pid ", sizeof s);
107 numlcat(s, (int)_thread_sys_getpid(), sizeof s);
108 strlcat(s, ": Fatal error '", sizeof s);
109 strlcat(s, string, sizeof s);
110 strlcat(s, "' at line ", sizeof s);
111 numlcat(s, lineno, sizeof s);
112 strlcat(s, " in file ", sizeof s);
113 strlcat(s, fname, sizeof s);
114 strlcat(s, " (errno = ", sizeof s);
115 numlcat(s, errno, sizeof s);
116 strlcat(s, ")\n", sizeof s);
117
118 /* Write the string to the standard error file descriptor: */
119 _thread_sys_write(2, s, strlen(s));
120
121 /* Force this process to exit: */
122 /* XXX - Do we want abort to be conditional on _PTHREADS_INVARIANTS? */
123 #if defined(_PTHREADS_INVARIANTS)
124 {
125 struct sigaction sa;
126 sigset_t s;
127
128 /* Ignore everything except ABORT */
129 sigfillset(&s);
130 sigdelset(&s, SIGABRT);
131 _thread_sys_sigprocmask(SIG_SETMASK, &s, NULL);
132
133 /* Set the abort handler to default (dump core) */
134 sa.sa_handler = SIG_DFL;
135 sigemptyset(&sa.sa_mask);
136 sa.sa_flags = SA_SIGINFO;
137 (void)_thread_sys_sigaction(SIGABRT, &sa, NULL);
138 (void)_thread_sys_kill(_thread_sys_getpid(), SIGABRT);
139 for (;;) ;
140 }
141 #else
142 _exit(1);
143 #endif
144 }
145
146 void
pthread_exit(void * status)147 pthread_exit(void *status)
148 {
149 struct pthread *curthread = _get_curthread();
150 pthread_t pthread;
151
152 /* Check if this thread is already in the process of exiting: */
153 if ((curthread->flags & PTHREAD_EXITING) != 0) {
154 PANIC("Thread has called pthread_exit() from a destructor. POSIX 1003.1 1996 s16.2.5.2 does not allow this!");
155 }
156
157 /* Flag this thread as exiting: */
158 curthread->flags |= PTHREAD_EXITING;
159
160 /* Save the return value: */
161 curthread->ret = status;
162
163 while (curthread->cleanup != NULL) {
164 pthread_cleanup_pop(1);
165 }
166 if (curthread->attr.cleanup_attr != NULL) {
167 curthread->attr.cleanup_attr(curthread->attr.arg_attr);
168 }
169 /* Check if there is thread specific data: */
170 if (curthread->specific_data != NULL) {
171 /* Run the thread-specific data destructors: */
172 _thread_cleanupspecific();
173 }
174
175 /*
176 * Lock the garbage collector mutex to ensure that the garbage
177 * collector is not using the dead thread list.
178 */
179 if (pthread_mutex_lock(&_gc_mutex) != 0)
180 PANIC("Cannot lock gc mutex");
181
182 /* Add this thread to the list of dead threads. */
183 TAILQ_INSERT_HEAD(&_dead_list, curthread, dle);
184
185 /*
186 * Signal the garbage collector thread that there is something
187 * to clean up.
188 */
189 if (pthread_cond_signal(&_gc_cond) != 0)
190 PANIC("Cannot signal gc cond");
191
192 /*
193 * Avoid a race condition where a scheduling signal can occur
194 * causing the garbage collector thread to run. If this happens,
195 * the current thread can be cleaned out from under us.
196 */
197 _thread_kern_sig_defer();
198
199 /* Unlock the garbage collector mutex: */
200 if (pthread_mutex_unlock(&_gc_mutex) != 0)
201 PANIC("Cannot unlock gc mutex");
202
203 /* Check if there is a thread joining this one: */
204 if (curthread->joiner != NULL) {
205 pthread = curthread->joiner;
206 curthread->joiner = NULL;
207
208 switch (pthread->suspended) {
209 case SUSP_JOIN:
210 /*
211 * The joining thread is suspended. Change the
212 * suspension state to make the thread runnable when it
213 * is resumed:
214 */
215 pthread->suspended = SUSP_NO;
216 break;
217 case SUSP_NO:
218 /* Make the joining thread runnable: */
219 PTHREAD_NEW_STATE(pthread, PS_RUNNING);
220 break;
221 default:
222 PANIC("Unreachable code reached");
223 }
224
225 /* Set the return value for the joining thread: */
226 pthread->join_status.ret = curthread->ret;
227 pthread->join_status.error = 0;
228 pthread->join_status.thread = NULL;
229
230 /* Make this thread collectable by the garbage collector. */
231 PTHREAD_ASSERT(((curthread->attr.flags & PTHREAD_DETACHED) ==
232 0), "Cannot join a detached thread");
233 curthread->attr.flags |= PTHREAD_DETACHED;
234 }
235
236 /* Remove this thread from the thread list: */
237 TAILQ_REMOVE(&_thread_list, curthread, tle);
238
239 /* This thread will never be re-scheduled. */
240 _thread_kern_sched_state(PS_DEAD, __FILE__, __LINE__);
241
242 /* This point should not be reached. */
243 PANIC("Dead thread has resumed");
244 }
245 #endif
246