Update.
[glibc.git] / linuxthreads / cancel.c
blob47c0bfee0411581d61e68d8e5e1503488bc2e047
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. */
15 /* Thread cancellation */
17 #include <errno.h>
18 #include <rpc/rpc.h>
19 #include "pthread.h"
20 #include "internals.h"
21 #include "spinlock.h"
22 #include "restart.h"
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
33 int pthread_setcancelstate(int state, int * oldstate)
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;
47 int pthread_setcanceltype(int type, int * oldtype)
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;
61 int pthread_cancel(pthread_t thread)
63 pthread_handle handle = thread_handle(thread);
64 int pid;
65 int dorestart = 0;
66 pthread_descr th;
67 pthread_extricate_if *pextricate;
68 int already_canceled;
70 __pthread_lock(&handle->h_lock, NULL);
71 if (invalid_handle(handle, thread)) {
72 __pthread_unlock(&handle->h_lock);
73 return ESRCH;
76 th = handle->h_descr;
78 already_canceled = th->p_canceled;
79 th->p_canceled = 1;
81 if (th->p_cancelstate == PTHREAD_CANCEL_DISABLE || already_canceled) {
82 __pthread_unlock(&handle->h_lock);
83 return 0;
86 pextricate = th->p_extricate;
87 pid = th->p_pid;
89 /* If the thread has registered an extrication interface, then
90 invoke the interface. If it returns 1, then we succeeded in
91 dequeuing the thread from whatever waiting object it was enqueued
92 with. In that case, it is our responsibility to wake it up.
93 And also to set the p_woken_by_cancel flag so the woken thread
94 can tell that it was woken by cancellation. */
96 if (pextricate != NULL) {
97 dorestart = pextricate->pu_extricate_func(pextricate->pu_object, th);
98 th->p_woken_by_cancel = dorestart;
101 __pthread_unlock(&handle->h_lock);
103 /* If the thread has suspended or is about to, then we unblock it by
104 issuing a restart, instead of a cancel signal. Otherwise we send
105 the cancel signal to unblock the thread from a cancellation point,
106 or to initiate asynchronous cancellation. The restart is needed so
107 we have proper accounting of restarts; suspend decrements the thread's
108 resume count, and restart() increments it. This also means that suspend's
109 handling of the cancel signal is obsolete. */
111 if (dorestart)
112 restart(th);
113 else
114 kill(pid, __pthread_sig_cancel);
116 return 0;
119 void pthread_testcancel(void)
121 pthread_descr self = thread_self();
122 if (THREAD_GETMEM(self, p_canceled)
123 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)
124 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
127 void _pthread_cleanup_push(struct _pthread_cleanup_buffer * buffer,
128 void (*routine)(void *), void * arg)
130 pthread_descr self = thread_self();
131 buffer->__routine = routine;
132 buffer->__arg = arg;
133 buffer->__prev = THREAD_GETMEM(self, p_cleanup);
134 if (buffer->__prev != NULL && FRAME_LEFT (buffer, buffer->__prev))
135 buffer->__prev = NULL;
136 THREAD_SETMEM(self, p_cleanup, buffer);
139 void _pthread_cleanup_pop(struct _pthread_cleanup_buffer * buffer,
140 int execute)
142 pthread_descr self = thread_self();
143 if (execute) buffer->__routine(buffer->__arg);
144 THREAD_SETMEM(self, p_cleanup, buffer->__prev);
147 void _pthread_cleanup_push_defer(struct _pthread_cleanup_buffer * buffer,
148 void (*routine)(void *), void * arg)
150 pthread_descr self = thread_self();
151 buffer->__routine = routine;
152 buffer->__arg = arg;
153 buffer->__canceltype = THREAD_GETMEM(self, p_canceltype);
154 buffer->__prev = THREAD_GETMEM(self, p_cleanup);
155 if (buffer->__prev != NULL && FRAME_LEFT (buffer, buffer->__prev))
156 buffer->__prev = NULL;
157 THREAD_SETMEM(self, p_canceltype, PTHREAD_CANCEL_DEFERRED);
158 THREAD_SETMEM(self, p_cleanup, buffer);
161 void _pthread_cleanup_pop_restore(struct _pthread_cleanup_buffer * buffer,
162 int execute)
164 pthread_descr self = thread_self();
165 if (execute) buffer->__routine(buffer->__arg);
166 THREAD_SETMEM(self, p_cleanup, buffer->__prev);
167 THREAD_SETMEM(self, p_canceltype, buffer->__canceltype);
168 if (THREAD_GETMEM(self, p_canceled) &&
169 THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
170 THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
171 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
174 void __pthread_perform_cleanup(char *currentframe)
176 pthread_descr self = thread_self();
177 struct _pthread_cleanup_buffer *c = THREAD_GETMEM(self, p_cleanup);
178 struct _pthread_cleanup_buffer *last;
180 if (c != NULL)
181 while (FRAME_LEFT (currentframe, c))
183 last = c;
184 c = c->__prev;
186 if (c == NULL || FRAME_LEFT (last, c))
188 c = NULL;
189 break;
193 while (c != NULL)
195 c->__routine(c->__arg);
197 last = c;
198 c = c->__prev;
200 if (FRAME_LEFT (last, c))
201 break;
204 /* And the TSD which needs special help. */
205 #if !(USE_TLS && HAVE___THREAD)
206 if (THREAD_GETMEM(self, p_libc_specific[_LIBC_TSD_KEY_RPC_VARS]) != NULL)
207 __rpc_thread_destroy ();
208 #else
209 if (__libc_tsd_get (RPC_VARS) != NULL)
210 __rpc_thread_destroy ();
211 #endif
214 #ifndef SHARED
215 /* We need a hook to force the cancelation wrappers and file locking
216 to be linked in when static libpthread is used. */
217 extern const int __pthread_provide_wrappers;
218 static const int *const __pthread_require_wrappers =
219 &__pthread_provide_wrappers;
220 extern const int __pthread_provide_lockfile;
221 static const int *const __pthread_require_lockfile =
222 &__pthread_provide_lockfile;
223 #endif