]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/uclibc/lib/contrib/uclibc/libpthread/linuxthreads/cancel.c
update
[l4.git] / l4 / pkg / uclibc / lib / contrib / uclibc / libpthread / linuxthreads / 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 "pthread.h"
19 #include "internals.h"
20 #include "spinlock.h"
21 #include "restart.h"
22
23 #ifdef _STACK_GROWS_DOWN
24 # define FRAME_LEFT(frame, other) ((char *) frame >= (char *) other)
25 #elif defined _STACK_GROWS_UP
26 # define FRAME_LEFT(frame, other) ((char *) frame <= (char *) other)
27 #else
28 # error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP"
29 #endif
30
31
32 int __pthread_setcancelstate(int state, int * oldstate)
33 {
34   pthread_descr self = thread_self();
35   if (state < PTHREAD_CANCEL_ENABLE || state > PTHREAD_CANCEL_DISABLE)
36     return EINVAL;
37   if (oldstate != NULL) *oldstate = THREAD_GETMEM(self, p_cancelstate);
38   THREAD_SETMEM(self, p_cancelstate, state);
39   if (THREAD_GETMEM(self, p_canceled) &&
40       THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
41       THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
42     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
43   return 0;
44 }
45 strong_alias (__pthread_setcancelstate, pthread_setcancelstate)
46
47 int __pthread_setcanceltype(int type, int * oldtype)
48 {
49   pthread_descr self = thread_self();
50   if (type < PTHREAD_CANCEL_DEFERRED || type > PTHREAD_CANCEL_ASYNCHRONOUS)
51     return EINVAL;
52   if (oldtype != NULL) *oldtype = THREAD_GETMEM(self, p_canceltype);
53   THREAD_SETMEM(self, p_canceltype, type);
54   if (THREAD_GETMEM(self, p_canceled) &&
55       THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
56       THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
57     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
58   return 0;
59 }
60 strong_alias (__pthread_setcanceltype, pthread_setcanceltype)
61
62
63 /* The next two functions are similar to pthread_setcanceltype() but
64    more specialized for the use in the cancelable functions like write().
65    They do not need to check parameters etc.  */
66 int
67 attribute_hidden
68 __pthread_enable_asynccancel (void)
69 {
70   pthread_descr self = thread_self();
71   int oldtype = THREAD_GETMEM(self, p_canceltype);
72   THREAD_SETMEM(self, p_canceltype, PTHREAD_CANCEL_ASYNCHRONOUS);
73   if (__builtin_expect (THREAD_GETMEM(self, p_canceled), 0) &&
74       THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)
75     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
76   return oldtype;
77 }
78
79 void
80 internal_function attribute_hidden
81 __pthread_disable_asynccancel (int oldtype)
82 {
83   pthread_descr self = thread_self();
84   THREAD_SETMEM(self, p_canceltype, oldtype);
85 }
86
87
88 int pthread_cancel(pthread_t thread)
89 {
90   pthread_handle handle = thread_handle(thread);
91   int pid;
92   int dorestart = 0;
93   pthread_descr th;
94   pthread_extricate_if *pextricate;
95   int already_canceled;
96
97   __pthread_lock(&handle->h_lock, NULL);
98   if (invalid_handle(handle, thread)) {
99     __pthread_unlock(&handle->h_lock);
100     return ESRCH;
101   }
102
103   th = handle->h_descr;
104
105   already_canceled = th->p_canceled;
106   th->p_canceled = 1;
107
108   if (th->p_cancelstate == PTHREAD_CANCEL_DISABLE || already_canceled) {
109     __pthread_unlock(&handle->h_lock);
110     return 0;
111   }
112
113   pextricate = th->p_extricate;
114   pid = th->p_pid;
115
116   /* If the thread has registered an extrication interface, then
117      invoke the interface. If it returns 1, then we succeeded in
118      dequeuing the thread from whatever waiting object it was enqueued
119      with. In that case, it is our responsibility to wake it up.
120      And also to set the p_woken_by_cancel flag so the woken thread
121      can tell that it was woken by cancellation. */
122
123   if (pextricate != NULL) {
124     dorestart = pextricate->pu_extricate_func(pextricate->pu_object, th);
125     th->p_woken_by_cancel = dorestart;
126   }
127
128   __pthread_unlock(&handle->h_lock);
129
130   /* If the thread has suspended or is about to, then we unblock it by
131      issuing a restart, instead of a cancel signal. Otherwise we send
132      the cancel signal to unblock the thread from a cancellation point,
133      or to initiate asynchronous cancellation. The restart is needed so
134      we have proper accounting of restarts; suspend decrements the thread's
135      resume count, and restart() increments it.  This also means that suspend's
136      handling of the cancel signal is obsolete. */
137
138   if (dorestart)
139     restart(th);
140   else
141     kill(pid, __pthread_sig_cancel);
142
143   return 0;
144 }
145
146 void pthread_testcancel(void)
147 {
148   pthread_descr self = thread_self();
149   if (THREAD_GETMEM(self, p_canceled)
150       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)
151     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
152 }
153
154 void _pthread_cleanup_push(struct _pthread_cleanup_buffer * buffer,
155                            void (*routine)(void *), void * arg)
156 {
157   pthread_descr self = thread_self();
158   buffer->__routine = routine;
159   buffer->__arg = arg;
160   buffer->__prev = THREAD_GETMEM(self, p_cleanup);
161   if (buffer->__prev != NULL && FRAME_LEFT (buffer, buffer->__prev))
162     buffer->__prev = NULL;
163   THREAD_SETMEM(self, p_cleanup, buffer);
164 }
165
166 void _pthread_cleanup_pop(struct _pthread_cleanup_buffer * buffer,
167                           int execute)
168 {
169   pthread_descr self = thread_self();
170   if (execute) buffer->__routine(buffer->__arg);
171   THREAD_SETMEM(self, p_cleanup, buffer->__prev);
172 }
173
174 void _pthread_cleanup_push_defer(struct _pthread_cleanup_buffer * buffer,
175                                  void (*routine)(void *), void * arg)
176 {
177   pthread_descr self = thread_self();
178   buffer->__routine = routine;
179   buffer->__arg = arg;
180   buffer->__canceltype = THREAD_GETMEM(self, p_canceltype);
181   buffer->__prev = THREAD_GETMEM(self, p_cleanup);
182   if (buffer->__prev != NULL && FRAME_LEFT (buffer, buffer->__prev))
183     buffer->__prev = NULL;
184   THREAD_SETMEM(self, p_canceltype, PTHREAD_CANCEL_DEFERRED);
185   THREAD_SETMEM(self, p_cleanup, buffer);
186 }
187
188 void _pthread_cleanup_pop_restore(struct _pthread_cleanup_buffer * buffer,
189                                   int execute)
190 {
191   pthread_descr self = thread_self();
192   if (execute) buffer->__routine(buffer->__arg);
193   THREAD_SETMEM(self, p_cleanup, buffer->__prev);
194   THREAD_SETMEM(self, p_canceltype, buffer->__canceltype);
195   if (THREAD_GETMEM(self, p_canceled) &&
196       THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
197       THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
198     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
199 }
200
201 extern void __rpc_thread_destroy(void);
202 void __pthread_perform_cleanup(char *currentframe)
203 {
204   pthread_descr self = thread_self();
205   struct _pthread_cleanup_buffer *c = THREAD_GETMEM(self, p_cleanup);
206   struct _pthread_cleanup_buffer *last;
207
208   if (c != NULL)
209     while (FRAME_LEFT (currentframe, c))
210       {
211         last = c;
212         c = c->__prev;
213
214         if (c == NULL || FRAME_LEFT (last, c))
215           {
216             c = NULL;
217             break;
218           }
219       }
220
221   while (c != NULL)
222     {
223       c->__routine(c->__arg);
224
225       last = c;
226       c = c->__prev;
227
228       if (FRAME_LEFT (last, c))
229         break;
230     }
231
232 #ifdef __UCLIBC_HAS_RPC__
233   /* And the TSD which needs special help.  */
234   if (THREAD_GETMEM(self, p_libc_specific[_LIBC_TSD_KEY_RPC_VARS]) != NULL)
235       __rpc_thread_destroy ();
236 #endif
237 }