1 /* Linuxthreads - a simple clone()-based implementation of Posix */
2 /* threads for Linux. */
3 /* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
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. */
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. */
15 /* Thread cancellation */
18 #include <libc-internal.h>
20 #include "internals.h"
24 #include <l4/sys/kdebug.h>
26 #ifdef _STACK_GROWS_DOWN
27 # define FRAME_LEFT(frame, other) ((char *) frame >= (char *) other)
29 # define FRAME_LEFT(frame, other) ((char *) frame <= (char *) other)
31 # error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP"
35 int __pthread_setcancelstate(int state, int * oldstate)
37 pthread_descr self = thread_self();
38 if (state < PTHREAD_CANCEL_ENABLE || state > PTHREAD_CANCEL_DISABLE)
40 if (oldstate != NULL) *oldstate = THREAD_GETMEM(self, p_cancelstate);
41 THREAD_SETMEM(self, p_cancelstate, state);
42 if (THREAD_GETMEM(self, p_canceled) &&
43 THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
44 THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
45 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
48 strong_alias (__pthread_setcancelstate, pthread_setcancelstate)
50 int __pthread_setcanceltype(int type, int * oldtype)
52 pthread_descr self = thread_self();
53 if (type < PTHREAD_CANCEL_DEFERRED || type > PTHREAD_CANCEL_ASYNCHRONOUS)
55 if (oldtype != NULL) *oldtype = THREAD_GETMEM(self, p_canceltype);
56 THREAD_SETMEM(self, p_canceltype, type);
57 if (THREAD_GETMEM(self, p_canceled) &&
58 THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
59 THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
60 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
63 strong_alias (__pthread_setcanceltype, pthread_setcanceltype)
66 /* The next two functions are similar to pthread_setcanceltype() but
67 more specialized for the use in the cancelable functions like write().
68 They do not need to check parameters etc. */
71 __pthread_enable_asynccancel (void)
73 pthread_descr self = thread_self();
74 int oldtype = THREAD_GETMEM(self, p_canceltype);
75 THREAD_SETMEM(self, p_canceltype, PTHREAD_CANCEL_ASYNCHRONOUS);
76 if (__builtin_expect (THREAD_GETMEM(self, p_canceled), 0) &&
77 THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)
78 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
83 internal_function attribute_hidden
84 __pthread_disable_asynccancel (int oldtype)
86 pthread_descr self = thread_self();
87 THREAD_SETMEM(self, p_canceltype, oldtype);
92 static void pthread_handle_sigcancel(void)
94 pthread_descr self = check_thread_self();
97 if (self == __manager_thread)
99 enter_kdebug("cancel of the manager hooooo");
101 __pthread_manager_sighandler(sig);
105 if (__builtin_expect (__pthread_exit_requested, 0)) {
107 /* Main thread should accumulate times for thread manager and its
108 children, so that timings for main thread account for all threads. */
109 if (self == __pthread_main_thread) {
111 waitpid(manager_thread->p_pid, NULL, __WCLONE);
113 waitpid(__pthread_manager_thread.p_pid, NULL, __WCLONE);
117 _exit(__pthread_exit_code);
119 if (__builtin_expect (THREAD_GETMEM(self, p_canceled), 0)
120 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
121 if (THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
122 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
123 jmpbuf = THREAD_GETMEM(self, p_cancel_jmp);
124 if (jmpbuf != NULL) {
125 THREAD_SETMEM(self, p_cancel_jmp, NULL);
126 siglongjmp(*jmpbuf, 1);
132 int pthread_cancel(pthread_t thread)
134 pthread_handle handle = thread_handle(thread);
138 pthread_extricate_if *pextricate;
139 int already_canceled;
141 __pthread_lock(handle_to_lock(handle), NULL);
142 if (invalid_handle(handle, thread)) {
143 __pthread_unlock(handle_to_lock(handle));
147 th = handle_to_descr(handle);
149 already_canceled = th->p_canceled;
152 if (th->p_cancelstate == PTHREAD_CANCEL_DISABLE || already_canceled) {
153 __pthread_unlock(handle_to_lock(handle));
157 pextricate = th->p_extricate;
160 /* If the thread has registered an extrication interface, then
161 invoke the interface. If it returns 1, then we succeeded in
162 dequeuing the thread from whatever waiting object it was enqueued
163 with. In that case, it is our responsibility to wake it up.
164 And also to set the p_woken_by_cancel flag so the woken thread
165 can tell that it was woken by cancellation. */
167 if (pextricate != NULL) {
168 dorestart = pextricate->pu_extricate_func(pextricate->pu_object, th);
169 th->p_woken_by_cancel = dorestart;
172 __pthread_unlock(handle_to_lock(handle));
174 /* If the thread has suspended or is about to, then we unblock it by
175 issuing a restart, instead of a cancel signal. Otherwise we send
176 the cancel signal to unblock the thread from a cancellation point,
177 or to initiate asynchronous cancellation. The restart is needed so
178 we have proper accounting of restarts; suspend decrements the thread's
179 resume count, and restart() increments it. This also means that suspend's
180 handling of the cancel signal is obsolete. */
184 else if (th->p_canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
186 l4_thread_ex_regs(th->p_th_cap, (l4_umword_t)&pthread_handle_sigcancel,
187 ~0UL, L4_THREAD_EX_REGS_CANCEL);
193 void pthread_testcancel(void)
195 pthread_descr self = thread_self();
196 if (THREAD_GETMEM(self, p_canceled)
197 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)
198 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
201 void _pthread_cleanup_push(struct _pthread_cleanup_buffer * buffer,
202 void (*routine)(void *), void * arg)
204 pthread_descr self = thread_self();
205 buffer->__routine = routine;
207 buffer->__prev = THREAD_GETMEM(self, p_cleanup);
208 if (buffer->__prev != NULL && FRAME_LEFT (buffer, buffer->__prev))
209 buffer->__prev = NULL;
210 THREAD_SETMEM(self, p_cleanup, buffer);
213 void _pthread_cleanup_pop(struct _pthread_cleanup_buffer * buffer,
216 pthread_descr self = thread_self();
217 if (execute) buffer->__routine(buffer->__arg);
218 THREAD_SETMEM(self, p_cleanup, buffer->__prev);
221 void _pthread_cleanup_push_defer(struct _pthread_cleanup_buffer * buffer,
222 void (*routine)(void *), void * arg)
224 pthread_descr self = thread_self();
225 buffer->__routine = routine;
227 buffer->__canceltype = THREAD_GETMEM(self, p_canceltype);
228 buffer->__prev = THREAD_GETMEM(self, p_cleanup);
229 if (buffer->__prev != NULL && FRAME_LEFT (buffer, buffer->__prev))
230 buffer->__prev = NULL;
231 THREAD_SETMEM(self, p_canceltype, PTHREAD_CANCEL_DEFERRED);
232 THREAD_SETMEM(self, p_cleanup, buffer);
235 void _pthread_cleanup_pop_restore(struct _pthread_cleanup_buffer * buffer,
238 pthread_descr self = thread_self();
239 if (execute) buffer->__routine(buffer->__arg);
240 THREAD_SETMEM(self, p_cleanup, buffer->__prev);
241 THREAD_SETMEM(self, p_canceltype, buffer->__canceltype);
242 if (THREAD_GETMEM(self, p_canceled) &&
243 THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
244 THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
245 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
248 extern void __rpc_thread_destroy(void);
249 void __pthread_perform_cleanup(char *currentframe)
251 pthread_descr self = thread_self();
252 struct _pthread_cleanup_buffer *c = THREAD_GETMEM(self, p_cleanup);
253 struct _pthread_cleanup_buffer *last;
256 while (FRAME_LEFT (currentframe, c))
261 if (c == NULL || FRAME_LEFT (last, c))
270 c->__routine(c->__arg);
275 if (FRAME_LEFT (last, c))
279 #ifdef __UCLIBC_HAS_RPC__
280 /* And the TSD which needs special help. */
281 if (THREAD_GETMEM(self, p_libc_specific[_LIBC_TSD_KEY_RPC_VARS]) != NULL)
282 __rpc_thread_destroy ();