]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/uclibc/lib/libpthread/src/cancel.c
update
[l4.git] / l4 / pkg / uclibc / lib / libpthread / src / cancel.c
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 cancellation */
16
17 #include <errno.h>
18 #include <libc-internal.h>
19 #include "pthread.h"
20 #include "internals.h"
21 #include "spinlock.h"
22 #include "restart.h"
23
24 #include <l4/sys/kdebug.h>
25
26 #ifdef _STACK_GROWS_DOWN
27 # define FRAME_LEFT(frame, other) ((char *) frame >= (char *) other)
28 #elif _STACK_GROWS_UP
29 # define FRAME_LEFT(frame, other) ((char *) frame <= (char *) other)
30 #else
31 # error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP"
32 #endif
33
34
35 int
36 attribute_hidden
37 __pthread_setcancelstate(int state, int * oldstate)
38 {
39   pthread_descr self = thread_self();
40   if (state < PTHREAD_CANCEL_ENABLE || state > PTHREAD_CANCEL_DISABLE)
41     return EINVAL;
42   if (oldstate != NULL) *oldstate = THREAD_GETMEM(self, p_cancelstate);
43   THREAD_SETMEM(self, p_cancelstate, state);
44   if (THREAD_GETMEM(self, p_canceled) &&
45       THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
46       THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
47     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
48   return 0;
49 }
50 strong_alias (__pthread_setcancelstate, pthread_setcancelstate)
51
52 int
53 attribute_hidden
54 __pthread_setcanceltype(int type, int * oldtype)
55 {
56   pthread_descr self = thread_self();
57   if (type < PTHREAD_CANCEL_DEFERRED || type > PTHREAD_CANCEL_ASYNCHRONOUS)
58     return EINVAL;
59   if (oldtype != NULL) *oldtype = THREAD_GETMEM(self, p_canceltype);
60   THREAD_SETMEM(self, p_canceltype, type);
61   if (THREAD_GETMEM(self, p_canceled) &&
62       THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
63       THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
64     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
65   return 0;
66 }
67 strong_alias (__pthread_setcanceltype, pthread_setcanceltype)
68
69
70 /* The next two functions are similar to pthread_setcanceltype() but
71    more specialized for the use in the cancelable functions like write().
72    They do not need to check parameters etc.  */
73 int
74 attribute_hidden
75 __pthread_enable_asynccancel (void)
76 {
77   pthread_descr self = thread_self();
78   int oldtype = THREAD_GETMEM(self, p_canceltype);
79   THREAD_SETMEM(self, p_canceltype, PTHREAD_CANCEL_ASYNCHRONOUS);
80   if (__builtin_expect (THREAD_GETMEM(self, p_canceled), 0) &&
81       THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)
82     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
83   return oldtype;
84 }
85
86 void
87 internal_function attribute_hidden
88 __pthread_disable_asynccancel (int oldtype)
89 {
90   pthread_descr self = thread_self();
91   THREAD_SETMEM(self, p_canceltype, oldtype);
92 }
93
94
95
96 static void pthread_handle_sigcancel(void)
97 {
98   pthread_descr self = check_thread_self();
99   sigjmp_buf * jmpbuf;
100
101   if (self == __manager_thread)
102     {
103       enter_kdebug("cancel of the manager hooooo");
104 #if 0
105       __pthread_manager_sighandler(sig);
106 #endif
107       return;
108     }
109   if (__builtin_expect (__pthread_exit_requested, 0)) {
110 #ifdef NOT_FOR_L4
111     /* Main thread should accumulate times for thread manager and its
112        children, so that timings for main thread account for all threads. */
113     if (self == __pthread_main_thread) {
114 #ifdef USE_TLS
115       waitpid(manager_thread->p_pid, NULL, __WCLONE);
116 #else
117       waitpid(__pthread_manager_thread.p_pid, NULL, __WCLONE);
118 #endif
119     }
120 #endif
121     _exit(__pthread_exit_code);
122   }
123   if (__builtin_expect (THREAD_GETMEM(self, p_canceled), 0)
124       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
125     if (THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
126       __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
127     jmpbuf = THREAD_GETMEM(self, p_cancel_jmp);
128     if (jmpbuf != NULL) {
129       THREAD_SETMEM(self, p_cancel_jmp, NULL);
130       siglongjmp(*jmpbuf, 1);
131     }
132   }
133 }
134
135
136 int pthread_cancel(pthread_t thread)
137 {
138   pthread_handle handle = thread_handle(thread);
139   int dorestart = 0;
140   pthread_descr th;
141   pthread_extricate_if *pextricate;
142   int already_canceled;
143
144   __pthread_lock(handle_to_lock(handle), NULL);
145   if (invalid_handle(handle, thread)) {
146     __pthread_unlock(handle_to_lock(handle));
147     return ESRCH;
148   }
149
150   th = handle_to_descr(handle);
151
152   already_canceled = th->p_canceled;
153   th->p_canceled = 1;
154
155   if (th->p_cancelstate == PTHREAD_CANCEL_DISABLE || already_canceled) {
156     __pthread_unlock(handle_to_lock(handle));
157     return 0;
158   }
159
160   pextricate = th->p_extricate;
161
162   /* If the thread has registered an extrication interface, then
163      invoke the interface. If it returns 1, then we succeeded in
164      dequeuing the thread from whatever waiting object it was enqueued
165      with. In that case, it is our responsibility to wake it up.
166      And also to set the p_woken_by_cancel flag so the woken thread
167      can tell that it was woken by cancellation. */
168
169   if (pextricate != NULL) {
170     dorestart = pextricate->pu_extricate_func(pextricate->pu_object, th);
171     th->p_woken_by_cancel = dorestart;
172   }
173
174   __pthread_unlock(handle_to_lock(handle));
175
176   /* If the thread has suspended or is about to, then we unblock it by
177      issuing a restart, instead of a cancel signal. Otherwise we send
178      the cancel signal to unblock the thread from a cancellation point,
179      or to initiate asynchronous cancellation. The restart is needed so
180      we have proper accounting of restarts; suspend decrements the thread's
181      resume count, and restart() increments it.  This also means that suspend's
182      handling of the cancel signal is obsolete. */
183
184   if (dorestart)
185     restart(th);
186   else if (th->p_canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
187     {
188       l4_thread_ex_regs(th->p_th_cap, (l4_umword_t)&pthread_handle_sigcancel,
189                         ~0UL, L4_THREAD_EX_REGS_CANCEL);
190     }
191
192   return 0;
193 }
194
195 void pthread_testcancel(void)
196 {
197   pthread_descr self = thread_self();
198   if (THREAD_GETMEM(self, p_canceled)
199       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)
200     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
201 }
202
203 void
204 attribute_hidden
205 __pthread_cleanup_push(struct _pthread_cleanup_buffer * buffer,
206                            void (*routine)(void *), void * arg)
207 {
208   pthread_descr self = thread_self();
209   buffer->__routine = routine;
210   buffer->__arg = arg;
211   buffer->__prev = THREAD_GETMEM(self, p_cleanup);
212   if (buffer->__prev != NULL && FRAME_LEFT (buffer, buffer->__prev))
213     buffer->__prev = NULL;
214   THREAD_SETMEM(self, p_cleanup, buffer);
215 }
216
217 strong_alias(__pthread_cleanup_push, _pthread_cleanup_push)
218
219 void
220 attribute_hidden
221 __pthread_cleanup_pop(struct _pthread_cleanup_buffer * buffer,
222                           int execute)
223 {
224   pthread_descr self = thread_self();
225   if (execute) buffer->__routine(buffer->__arg);
226   THREAD_SETMEM(self, p_cleanup, buffer->__prev);
227 }
228
229 strong_alias(__pthread_cleanup_pop, _pthread_cleanup_pop)
230
231 void
232 attribute_hidden
233 __pthread_cleanup_push_defer(struct _pthread_cleanup_buffer * buffer,
234                                  void (*routine)(void *), void * arg)
235 {
236   pthread_descr self = thread_self();
237   buffer->__routine = routine;
238   buffer->__arg = arg;
239   buffer->__canceltype = THREAD_GETMEM(self, p_canceltype);
240   buffer->__prev = THREAD_GETMEM(self, p_cleanup);
241   if (buffer->__prev != NULL && FRAME_LEFT (buffer, buffer->__prev))
242     buffer->__prev = NULL;
243   THREAD_SETMEM(self, p_canceltype, PTHREAD_CANCEL_DEFERRED);
244   THREAD_SETMEM(self, p_cleanup, buffer);
245 }
246
247 strong_alias(__pthread_cleanup_push_defer, _pthread_cleanup_push_defer)
248
249 void
250 attribute_hidden
251 __pthread_cleanup_pop_restore(struct _pthread_cleanup_buffer * buffer,
252                                   int execute)
253 {
254   pthread_descr self = thread_self();
255   if (execute) buffer->__routine(buffer->__arg);
256   THREAD_SETMEM(self, p_cleanup, buffer->__prev);
257   THREAD_SETMEM(self, p_canceltype, buffer->__canceltype);
258   if (THREAD_GETMEM(self, p_canceled) &&
259       THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
260       THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
261     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
262 }
263
264 strong_alias(__pthread_cleanup_pop_restore, _pthread_cleanup_pop_restore)
265
266 extern void __rpc_thread_destroy(void);
267 void
268 attribute_hidden internal_function
269 __pthread_perform_cleanup(char *currentframe)
270 {
271   pthread_descr self = thread_self();
272   struct _pthread_cleanup_buffer *c = THREAD_GETMEM(self, p_cleanup);
273   struct _pthread_cleanup_buffer *last;
274
275   if (c != NULL)
276     while (FRAME_LEFT (currentframe, c))
277       {
278         last = c;
279         c = c->__prev;
280
281         if (c == NULL || FRAME_LEFT (last, c))
282           {
283             c = NULL;
284             break;
285           }
286       }
287
288   while (c != NULL)
289     {
290       c->__routine(c->__arg);
291
292       last = c;
293       c = c->__prev;
294
295       if (FRAME_LEFT (last, c))
296         break;
297     }
298
299 #ifdef __UCLIBC_HAS_RPC__
300   /* And the TSD which needs special help.  */
301   if (THREAD_GETMEM(self, p_libc_specific[_LIBC_TSD_KEY_RPC_VARS]) != NULL)
302       __rpc_thread_destroy ();
303 #endif
304 }