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