1 /* Linuxthreads - a simple clone()-based implementation of Posix */
2 /* threads for Linux. */
3 /* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
4 /* */
5 /* This program is free software; you can redistribute it and/or */
6 /* modify it under the terms of the GNU Library General Public License */
7 /* as published by the Free Software Foundation; either version 2 */
8 /* of the License, or (at your option) any later version. */
9 /* */
10 /* This program is distributed in the hope that it will be useful, */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
13 /* GNU Library General Public License for more details. */
14
15 /* Thread termination and joining */
16
17 #include <features.h>
18 #include <errno.h>
19 #include <sched.h>
20 #include <unistd.h>
21 #include <stdlib.h>
22 #include "pthread.h"
23 #include "internals.h"
24 #include "spinlock.h"
25 #include "restart.h"
26 #include "debug.h" /* PDEBUG, added by StS */
27
libpthread_hidden_proto(pthread_exit)28 libpthread_hidden_proto(pthread_exit)
29 void pthread_exit(void * retval)
30 {
31 __pthread_do_exit (retval, CURRENT_STACK_FRAME);
32 }
libpthread_hidden_def(pthread_exit)33 libpthread_hidden_def (pthread_exit)
34
35 void __pthread_do_exit(void *retval, char *currentframe)
36 {
37 pthread_descr self = thread_self();
38 pthread_descr joining;
39 struct pthread_request request;
40 PDEBUG("self=%p, pid=%d\n", self, self->p_pid);
41
42 /* obey POSIX behavior and prevent cancellation functions from
43 * being called more than once.
44 * http://sourceware.org/ml/libc-ports/2006-10/msg00043.html
45 */
46 THREAD_SETMEM(self, p_cancelstate, PTHREAD_CANCEL_DISABLE);
47 THREAD_SETMEM(self, p_canceltype, PTHREAD_CANCEL_DEFERRED);
48
49 /* Call cleanup functions and destroy the thread-specific data */
50 __pthread_perform_cleanup(currentframe);
51 __pthread_destroy_specifics();
52 /* Store return value */
53 __pthread_lock(THREAD_GETMEM(self, p_lock), self);
54 THREAD_SETMEM(self, p_retval, retval);
55 /* See whether we have to signal the death. */
56 if (THREAD_GETMEM(self, p_report_events))
57 {
58 /* See whether TD_DEATH is in any of the mask. */
59 int idx = __td_eventword (TD_DEATH);
60 uint32_t mask = __td_eventmask (TD_DEATH);
61
62 if ((mask & (__pthread_threads_events.event_bits[idx]
63 | THREAD_GETMEM_NC(self,
64 p_eventbuf.eventmask).event_bits[idx]))
65 != 0)
66 {
67 /* Yep, we have to signal the death. */
68 THREAD_SETMEM(self, p_eventbuf.eventnum, TD_DEATH);
69 THREAD_SETMEM(self, p_eventbuf.eventdata, self);
70 __pthread_last_event = self;
71
72 /* Now call the function to signal the event. */
73 __linuxthreads_death_event();
74 }
75 }
76 /* Say that we've terminated */
77 THREAD_SETMEM(self, p_terminated, 1);
78 /* See if someone is joining on us */
79 joining = THREAD_GETMEM(self, p_joining);
80 PDEBUG("joining = %p, pid=%d\n", joining, joining ? joining->p_pid : 0);
81 __pthread_unlock(THREAD_GETMEM(self, p_lock));
82 /* Restart joining thread if any */
83 if (joining != NULL) restart(joining);
84 /* If this is the initial thread, block until all threads have terminated.
85 If another thread calls exit, we'll be terminated from our signal
86 handler. */
87 if (self == __pthread_main_thread && __pthread_manager_request >= 0) {
88 request.req_thread = self;
89 request.req_kind = REQ_MAIN_THREAD_EXIT;
90 TEMP_FAILURE_RETRY(write(__pthread_manager_request,
91 (char *)&request, sizeof(request)));
92 suspend(self);
93 /* Main thread flushes stdio streams and runs atexit functions.
94 * It also calls a handler within LinuxThreads which sends a process exit
95 * request to the thread manager. */
96 exit(0);
97 }
98 /* Exit the process (but don't flush stdio streams, and don't run
99 atexit functions). */
100 _exit(0);
101 }
102
103 /* Function called by pthread_cancel to remove the thread from
104 waiting on a condition variable queue. */
105
join_extricate_func(void * obj,pthread_descr th attribute_unused)106 static int join_extricate_func(void *obj, pthread_descr th attribute_unused)
107 {
108 volatile pthread_descr self = thread_self();
109 pthread_handle handle = obj;
110 pthread_descr jo;
111 int did_remove = 0;
112
113 __pthread_lock(&handle->h_lock, self);
114 jo = handle->h_descr;
115 did_remove = jo->p_joining != NULL;
116 jo->p_joining = NULL;
117 __pthread_unlock(&handle->h_lock);
118
119 return did_remove;
120 }
121
pthread_join(pthread_t thread_id,void ** thread_return)122 int pthread_join(pthread_t thread_id, void ** thread_return)
123 {
124 volatile pthread_descr self = thread_self();
125 struct pthread_request request;
126 pthread_handle handle = thread_handle(thread_id);
127 pthread_descr th;
128 pthread_extricate_if extr;
129 int already_canceled = 0;
130 PDEBUG("\n");
131
132 /* Set up extrication interface */
133 extr.pu_object = handle;
134 extr.pu_extricate_func = join_extricate_func;
135
136 __pthread_lock(&handle->h_lock, self);
137 if (invalid_handle(handle, thread_id)) {
138 __pthread_unlock(&handle->h_lock);
139 return ESRCH;
140 }
141 th = handle->h_descr;
142 if (th == self) {
143 __pthread_unlock(&handle->h_lock);
144 return EDEADLK;
145 }
146 /* If detached or already joined, error */
147 if (th->p_detached || th->p_joining != NULL) {
148 __pthread_unlock(&handle->h_lock);
149 return EINVAL;
150 }
151 /* If not terminated yet, suspend ourselves. */
152 if (! th->p_terminated) {
153 /* Register extrication interface */
154 __pthread_set_own_extricate_if(self, &extr);
155 if (!(THREAD_GETMEM(self, p_canceled)
156 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
157 th->p_joining = self;
158 else
159 already_canceled = 1;
160 __pthread_unlock(&handle->h_lock);
161
162 if (already_canceled) {
163 __pthread_set_own_extricate_if(self, 0);
164 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
165 }
166
167 PDEBUG("before suspend\n");
168 suspend(self);
169 PDEBUG("after suspend\n");
170 /* Deregister extrication interface */
171 __pthread_set_own_extricate_if(self, 0);
172
173 /* This is a cancellation point */
174 if (THREAD_GETMEM(self, p_woken_by_cancel)
175 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
176 THREAD_SETMEM(self, p_woken_by_cancel, 0);
177 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
178 }
179 __pthread_lock(&handle->h_lock, self);
180 }
181 /* Get return value */
182 if (thread_return != NULL) *thread_return = th->p_retval;
183 __pthread_unlock(&handle->h_lock);
184 /* Send notification to thread manager */
185 if (__pthread_manager_request >= 0) {
186 request.req_thread = self;
187 request.req_kind = REQ_FREE;
188 request.req_args.free.thread_id = thread_id;
189 TEMP_FAILURE_RETRY(write(__pthread_manager_request,
190 (char *) &request, sizeof(request)));
191 }
192 return 0;
193 }
194
pthread_tryjoin_np(pthread_t thread_id,void ** thread_return)195 int pthread_tryjoin_np(pthread_t thread_id, void ** thread_return)
196 {
197 volatile pthread_descr self = thread_self();
198 struct pthread_request request;
199 pthread_handle handle = thread_handle(thread_id);
200 pthread_descr th;
201 int result = 0;
202
203 /* Make sure the descriptor is valid. */
204 __pthread_lock(&handle->h_lock, self);
205 if (invalid_handle(handle, thread_id)) {
206 result = ESRCH;
207 goto err;
208 }
209 th = handle->h_descr;
210 /* Is the thread joinable?. */
211 if (th->p_detached || th->p_joining != NULL) {
212 result = EINVAL;
213 goto err;
214 }
215 if (th == self) {
216 result = EDEADLK;
217 goto err;
218 }
219 /* Return right away if the thread hasn't terminated yet. */
220 if (! th->p_terminated) {
221 result = EBUSY;
222 goto err;
223 }
224
225 /* Get return value */
226 if (thread_return != NULL) *thread_return = th->p_retval;
227 __pthread_unlock(&handle->h_lock);
228 /* Send notification to thread manager */
229 if (__pthread_manager_request >= 0) {
230 request.req_thread = self;
231 request.req_kind = REQ_FREE;
232 request.req_args.free.thread_id = thread_id;
233 TEMP_FAILURE_RETRY(write(__pthread_manager_request,
234 (char *) &request, sizeof(request)));
235 }
236 return 0;
237
238 err:
239 __pthread_unlock(&handle->h_lock);
240 return result;
241 }
242
pthread_timedjoin_np(pthread_t thread_id,void ** thread_return,const struct timespec * abstime)243 int pthread_timedjoin_np(pthread_t thread_id, void ** thread_return,
244 const struct timespec *abstime)
245 {
246 volatile pthread_descr self = thread_self();
247 struct pthread_request request;
248 pthread_handle handle = thread_handle(thread_id);
249 pthread_descr th;
250 pthread_extricate_if extr;
251 int already_canceled = 0;
252 int result = 0;
253 PDEBUG("\n");
254
255 /* Set up extrication interface */
256 extr.pu_object = handle;
257 extr.pu_extricate_func = join_extricate_func;
258
259 __pthread_lock(&handle->h_lock, self);
260 if (invalid_handle(handle, thread_id)) {
261 result = ESRCH;
262 goto err;
263 }
264 th = handle->h_descr;
265 if (th == self) {
266 result = EDEADLK;
267 goto err;
268 }
269 /* If detached or already joined, error */
270 if (th->p_detached || th->p_joining != NULL) {
271 result = EINVAL;
272 goto err;
273 }
274 /* If not terminated yet, suspend ourselves. */
275 if (! th->p_terminated) {
276 /* Register extrication interface */
277 __pthread_set_own_extricate_if(self, &extr);
278 if (!(THREAD_GETMEM(self, p_canceled)
279 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
280 th->p_joining = self;
281 else
282 already_canceled = 1;
283 __pthread_unlock(&handle->h_lock);
284
285 if (already_canceled) {
286 __pthread_set_own_extricate_if(self, 0);
287 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
288 }
289
290 PDEBUG("before suspend\n");
291 result = (timedsuspend(self, abstime) == 0) ? ETIMEDOUT : 0;
292 PDEBUG("after suspend\n");
293 /* Deregister extrication interface */
294 __pthread_set_own_extricate_if(self, 0);
295
296 /* This is a cancellation point */
297 if (result == 0
298 && THREAD_GETMEM(self, p_woken_by_cancel)
299 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
300 THREAD_SETMEM(self, p_woken_by_cancel, 0);
301 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
302 }
303 __pthread_lock(&handle->h_lock, self);
304 }
305
306 /* We might have timed out. */
307 if (result == 0) {
308 /* Get return value */
309 if (thread_return != NULL) *thread_return = th->p_retval;
310 }
311 else
312 th->p_joining = NULL;
313
314 __pthread_unlock(&handle->h_lock);
315
316 if (result == 0) {
317 /* Send notification to thread manager */
318 if (__pthread_manager_request >= 0) {
319 request.req_thread = self;
320 request.req_kind = REQ_FREE;
321 request.req_args.free.thread_id = thread_id;
322 TEMP_FAILURE_RETRY(write(__pthread_manager_request,
323 (char *) &request, sizeof(request)));
324 }
325 }
326 return result;
327
328 err:
329 __pthread_unlock(&handle->h_lock);
330 return result;
331 }
332
pthread_detach(pthread_t thread_id)333 int pthread_detach(pthread_t thread_id)
334 {
335 int terminated;
336 struct pthread_request request;
337 pthread_handle handle = thread_handle(thread_id);
338 pthread_descr th;
339
340 __pthread_lock(&handle->h_lock, NULL);
341 if (invalid_handle(handle, thread_id)) {
342 __pthread_unlock(&handle->h_lock);
343 return ESRCH;
344 }
345 th = handle->h_descr;
346 /* If already detached, error */
347 if (th->p_detached) {
348 __pthread_unlock(&handle->h_lock);
349 return EINVAL;
350 }
351 /* If already joining, don't do anything. */
352 if (th->p_joining != NULL) {
353 __pthread_unlock(&handle->h_lock);
354 return 0;
355 }
356 /* Mark as detached */
357 th->p_detached = 1;
358 terminated = th->p_terminated;
359 __pthread_unlock(&handle->h_lock);
360 /* If already terminated, notify thread manager to reclaim resources */
361 if (terminated && __pthread_manager_request >= 0) {
362 request.req_thread = thread_self();
363 request.req_kind = REQ_FREE;
364 request.req_args.free.thread_id = thread_id;
365 TEMP_FAILURE_RETRY(write(__pthread_manager_request,
366 (char *) &request, sizeof(request)));
367 }
368 return 0;
369 }
370