]> 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 __pthread_setcancelstate(int state, int * oldstate)
36 {
37   pthread_descr self = thread_self();
38   if (state < PTHREAD_CANCEL_ENABLE || state > PTHREAD_CANCEL_DISABLE)
39     return EINVAL;
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);
46   return 0;
47 }
48 strong_alias (__pthread_setcancelstate, pthread_setcancelstate)
49
50 int __pthread_setcanceltype(int type, int * oldtype)
51 {
52   pthread_descr self = thread_self();
53   if (type < PTHREAD_CANCEL_DEFERRED || type > PTHREAD_CANCEL_ASYNCHRONOUS)
54     return EINVAL;
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);
61   return 0;
62 }
63 strong_alias (__pthread_setcanceltype, pthread_setcanceltype)
64
65
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.  */
69 int
70 attribute_hidden
71 __pthread_enable_asynccancel (void)
72 {
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);
79   return oldtype;
80 }
81
82 void
83 internal_function attribute_hidden
84 __pthread_disable_asynccancel (int oldtype)
85 {
86   pthread_descr self = thread_self();
87   THREAD_SETMEM(self, p_canceltype, oldtype);
88 }
89
90
91
92 static void pthread_handle_sigcancel(void)
93 {
94   pthread_descr self = check_thread_self();
95   sigjmp_buf * jmpbuf;
96
97   if (self == __manager_thread)
98     {
99       enter_kdebug("cancel of the manager hooooo");
100 #if 0
101       __pthread_manager_sighandler(sig);
102 #endif
103       return;
104     }
105   if (__builtin_expect (__pthread_exit_requested, 0)) {
106 #ifdef NOT_FOR_L4
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) {
110 #ifdef USE_TLS
111       waitpid(manager_thread->p_pid, NULL, __WCLONE);
112 #else
113       waitpid(__pthread_manager_thread.p_pid, NULL, __WCLONE);
114 #endif
115     }
116 #endif
117     _exit(__pthread_exit_code);
118   }
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);
127     }
128   }
129 }
130
131
132 int pthread_cancel(pthread_t thread)
133 {
134   pthread_handle handle = thread_handle(thread);
135   l4_cap_idx_t cap;
136   int dorestart = 0;
137   pthread_descr th;
138   pthread_extricate_if *pextricate;
139   int already_canceled;
140
141   __pthread_lock(handle_to_lock(handle), NULL);
142   if (invalid_handle(handle, thread)) {
143     __pthread_unlock(handle_to_lock(handle));
144     return ESRCH;
145   }
146
147   th = handle_to_descr(handle);
148
149   already_canceled = th->p_canceled;
150   th->p_canceled = 1;
151
152   if (th->p_cancelstate == PTHREAD_CANCEL_DISABLE || already_canceled) {
153     __pthread_unlock(handle_to_lock(handle));
154     return 0;
155   }
156
157   pextricate = th->p_extricate;
158   cap = th->p_th_cap;
159
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. */
166
167   if (pextricate != NULL) {
168     dorestart = pextricate->pu_extricate_func(pextricate->pu_object, th);
169     th->p_woken_by_cancel = dorestart;
170   }
171
172   __pthread_unlock(handle_to_lock(handle));
173
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. */
181
182   if (dorestart)
183     restart(th);
184   else if (th->p_canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
185     {
186       l4_thread_ex_regs(th->p_th_cap, (l4_umword_t)&pthread_handle_sigcancel,
187                         ~0UL, L4_THREAD_EX_REGS_CANCEL);
188     }
189
190   return 0;
191 }
192
193 void pthread_testcancel(void)
194 {
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);
199 }
200
201 void _pthread_cleanup_push(struct _pthread_cleanup_buffer * buffer,
202                            void (*routine)(void *), void * arg)
203 {
204   pthread_descr self = thread_self();
205   buffer->__routine = routine;
206   buffer->__arg = arg;
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);
211 }
212
213 void _pthread_cleanup_pop(struct _pthread_cleanup_buffer * buffer,
214                           int execute)
215 {
216   pthread_descr self = thread_self();
217   if (execute) buffer->__routine(buffer->__arg);
218   THREAD_SETMEM(self, p_cleanup, buffer->__prev);
219 }
220
221 void _pthread_cleanup_push_defer(struct _pthread_cleanup_buffer * buffer,
222                                  void (*routine)(void *), void * arg)
223 {
224   pthread_descr self = thread_self();
225   buffer->__routine = routine;
226   buffer->__arg = arg;
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);
233 }
234
235 void _pthread_cleanup_pop_restore(struct _pthread_cleanup_buffer * buffer,
236                                   int execute)
237 {
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);
246 }
247
248 extern void __rpc_thread_destroy(void);
249 void __pthread_perform_cleanup(char *currentframe)
250 {
251   pthread_descr self = thread_self();
252   struct _pthread_cleanup_buffer *c = THREAD_GETMEM(self, p_cleanup);
253   struct _pthread_cleanup_buffer *last;
254
255   if (c != NULL)
256     while (FRAME_LEFT (currentframe, c))
257       {
258         last = c;
259         c = c->__prev;
260
261         if (c == NULL || FRAME_LEFT (last, c))
262           {
263             c = NULL;
264             break;
265           }
266       }
267
268   while (c != NULL)
269     {
270       c->__routine(c->__arg);
271
272       last = c;
273       c = c->__prev;
274
275       if (FRAME_LEFT (last, c))
276         break;
277     }
278
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 ();
283 #endif
284 }