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 <errno.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include "pthread.h"
21 #include "internals.h"
22 #include "spinlock.h"
23 #include "restart.h"
24
25 #include <l4/util/util.h>
26
27 void
28 attribute_hidden
__pthread_exit(void * retval)29 __pthread_exit(void * retval)
30 {
31 __pthread_do_exit (retval, CURRENT_STACK_FRAME);
32 }
strong_alias(__pthread_exit,pthread_exit)33 strong_alias (__pthread_exit, pthread_exit)
34
35 void
36 attribute_hidden
37 __pthread_do_exit(void *retval, char *currentframe)
38 {
39 pthread_descr self = thread_self();
40 pthread_descr joining;
41 struct pthread_request request;
42
43 /* Reset the cancellation flag to avoid looping if the cleanup handlers
44 contain cancellation points */
45 THREAD_SETMEM(self, p_canceled, 0);
46 /* Call cleanup functions and destroy the thread-specific data */
47 __pthread_perform_cleanup(currentframe);
48 __pthread_destroy_specifics();
49 /* Store return value */
50 __pthread_lock(THREAD_GETMEM(self, p_lock), self);
51 THREAD_SETMEM(self, p_retval, retval);
52 /* Say that we've terminated */
53 THREAD_SETMEM(self, p_terminated, 1);
54 /* See if someone is joining on us */
55 joining = THREAD_GETMEM(self, p_joining);
56 __pthread_unlock(THREAD_GETMEM(self, p_lock));
57 /* Restart joining thread if any */
58 if (joining != NULL)
59 {
60 restart(joining);
61 l4_sleep_forever();
62 }
63
64 /* If this is the initial thread, block until all threads have terminated.
65 If another thread calls exit, we'll be terminated from our signal
66 handler. */
67 if (self == __pthread_main_thread && __pthread_manager_request >= 0)
68 {
69 request.req_thread = self;
70 request.req_kind = REQ_MAIN_THREAD_EXIT;
71 __pthread_send_manager_rq(&request, 1);
72 /* Main thread flushes stdio streams and runs atexit functions.
73 It also calls a handler within LinuxThreads which sends a process exit
74 request to the thread manager. */
75 exit(0);
76 }
77 /* Threads other than the main one terminate without flushing stdio streams
78 or running atexit functions. */
79
80 //_exit(0);
81
82 request.req_thread = self;
83 request.req_kind = REQ_THREAD_EXIT;
84 __pthread_send_manager_rq(&request, 1);
85
86 l4_sleep_forever();
87 }
88
89 /* Function called by pthread_cancel to remove the thread from
90 waiting on a condition variable queue. */
91
join_extricate_func(void * obj,pthread_descr th)92 static int join_extricate_func(void *obj, pthread_descr th)
93 {
94 __volatile__ pthread_descr self = thread_self();
95 pthread_handle handle = obj;
96 pthread_descr jo;
97 int did_remove = 0;
98
99 __pthread_lock(handle_to_lock(handle), self);
100 jo = handle_to_descr(handle);
101 did_remove = jo->p_joining != NULL;
102 jo->p_joining = NULL;
103 __pthread_unlock(handle_to_lock(handle));
104
105 return did_remove;
106 }
107
pthread_join(pthread_t thread_id,void ** thread_return)108 int pthread_join(pthread_t thread_id, void ** thread_return)
109 {
110 __volatile__ pthread_descr self = thread_self();
111 struct pthread_request request;
112 pthread_handle handle = thread_handle(thread_id);
113 pthread_descr th;
114 pthread_extricate_if extr;
115 int already_canceled = 0;
116
117 /* Set up extrication interface */
118 extr.pu_object = handle;
119 extr.pu_extricate_func = join_extricate_func;
120
121 __pthread_lock(handle_to_lock(handle), self);
122 if (nonexisting_handle(handle, thread_id)) {
123 __pthread_unlock(handle_to_lock(handle));
124 return ESRCH;
125 }
126 th = handle_to_descr(handle);
127 if (th == self) {
128 __pthread_unlock(handle_to_lock(handle));
129 return EDEADLK;
130 }
131 /* If detached or already joined, error */
132 if (th->p_detached || th->p_joining != NULL) {
133 __pthread_unlock(handle_to_lock(handle));
134 return EINVAL;
135 }
136 /* If not terminated yet, suspend ourselves. */
137 if (! th->p_terminated) {
138 /* Register extrication interface */
139 __pthread_set_own_extricate_if(self, &extr);
140 if (!(THREAD_GETMEM(self, p_canceled)
141 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
142 th->p_joining = self;
143 else
144 already_canceled = 1;
145 __pthread_unlock(handle_to_lock(handle));
146
147 if (already_canceled) {
148 __pthread_set_own_extricate_if(self, 0);
149 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
150 }
151
152 suspend(self);
153 /* Deregister extrication interface */
154 __pthread_set_own_extricate_if(self, 0);
155
156 /* This is a cancellation point */
157 if (THREAD_GETMEM(self, p_woken_by_cancel)
158 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
159 THREAD_SETMEM(self, p_woken_by_cancel, 0);
160 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
161 }
162 __pthread_lock(handle_to_lock(handle), self);
163 }
164 /* Get return value */
165 if (thread_return != NULL) *thread_return = th->p_retval;
166 __pthread_unlock(handle_to_lock(handle));
167 /* Send notification to thread manager */
168 if (__pthread_manager_request >= 0) {
169 request.req_thread = self;
170 request.req_kind = REQ_FREE;
171 request.req_args.free.thread_id = thread_id;
172 __pthread_send_manager_rq(&request, 0);
173 }
174 return 0;
175 }
176
pthread_detach(pthread_t thread_id)177 int pthread_detach(pthread_t thread_id)
178 {
179 int terminated;
180 struct pthread_request request;
181 pthread_handle handle = thread_handle(thread_id);
182 pthread_descr th;
183
184 __pthread_lock(handle_to_lock(handle), NULL);
185 if (nonexisting_handle(handle, thread_id)) {
186 __pthread_unlock(handle_to_lock(handle));
187 return ESRCH;
188 }
189 th = handle_to_descr(handle);
190 /* If already detached, error */
191 if (th->p_detached) {
192 __pthread_unlock(handle_to_lock(handle));
193 return EINVAL;
194 }
195 /* If already joining, don't do anything. */
196 if (th->p_joining != NULL) {
197 __pthread_unlock(handle_to_lock(handle));
198 return 0;
199 }
200 /* Mark as detached */
201 th->p_detached = 1;
202 terminated = th->p_terminated;
203 __pthread_unlock(handle_to_lock(handle));
204 /* If already terminated, notify thread manager to reclaim resources */
205 if (terminated && __pthread_manager_request >= 0) {
206 request.req_thread = thread_self();
207 request.req_kind = REQ_FREE;
208 request.req_args.free.thread_id = thread_id;
209 __pthread_send_manager_rq(&request, 0);
210 }
211 return 0;
212 }
213