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 #include <bits/stackinfo.h>
23
24 #include <stdio.h>
25
26 #ifdef _STACK_GROWS_DOWN
27 # define FRAME_LEFT(frame, other) ((char *) frame >= (char *) other)
28 #elif defined _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 libpthread_hidden_proto(pthread_setcancelstate)
libpthread_hidden_proto(pthread_setcanceltype)35 libpthread_hidden_proto(pthread_setcanceltype)
36
37 int 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 }
libpthread_hidden_def(pthread_setcancelstate)50 libpthread_hidden_def(pthread_setcancelstate)
51
52 int pthread_setcanceltype(int type, int * oldtype)
53 {
54 pthread_descr self = thread_self();
55 if (type < PTHREAD_CANCEL_DEFERRED || type > PTHREAD_CANCEL_ASYNCHRONOUS)
56 return EINVAL;
57 if (oldtype != NULL) *oldtype = THREAD_GETMEM(self, p_canceltype);
58 THREAD_SETMEM(self, p_canceltype, type);
59 if (THREAD_GETMEM(self, p_canceled) &&
60 THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
61 THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
62 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
63 return 0;
64 }
libpthread_hidden_def(pthread_setcanceltype)65 libpthread_hidden_def(pthread_setcanceltype)
66
67 int pthread_cancel(pthread_t thread)
68 {
69 pthread_handle handle = thread_handle(thread);
70 int pid;
71 int dorestart = 0;
72 pthread_descr th;
73 pthread_extricate_if *pextricate;
74 int already_canceled;
75
76 __pthread_lock(&handle->h_lock, NULL);
77 if (invalid_handle(handle, thread)) {
78 __pthread_unlock(&handle->h_lock);
79 return ESRCH;
80 }
81
82 th = handle->h_descr;
83
84 already_canceled = th->p_canceled;
85 th->p_canceled = 1;
86
87 if (th->p_cancelstate == PTHREAD_CANCEL_DISABLE || already_canceled) {
88 __pthread_unlock(&handle->h_lock);
89 return 0;
90 }
91
92 pextricate = th->p_extricate;
93 pid = th->p_pid;
94
95 /* If the thread has registered an extrication interface, then
96 invoke the interface. If it returns 1, then we succeeded in
97 dequeuing the thread from whatever waiting object it was enqueued
98 with. In that case, it is our responsibility to wake it up.
99 And also to set the p_woken_by_cancel flag so the woken thread
100 can tell that it was woken by cancellation. */
101
102 if (pextricate != NULL) {
103 dorestart = pextricate->pu_extricate_func(pextricate->pu_object, th);
104 th->p_woken_by_cancel = dorestart;
105 }
106
107 __pthread_unlock(&handle->h_lock);
108
109 /* If the thread has suspended or is about to, then we unblock it by
110 issuing a restart, instead of a cancel signal. Otherwise we send
111 the cancel signal to unblock the thread from a cancellation point,
112 or to initiate asynchronous cancellation. The restart is needed so
113 we have proper accounting of restarts; suspend decrements the thread's
114 resume count, and restart() increments it. This also means that suspend's
115 handling of the cancel signal is obsolete. */
116
117 if (dorestart)
118 restart(th);
119 else
120 kill(pid, __pthread_sig_cancel);
121
122 return 0;
123 }
124
pthread_testcancel(void)125 void pthread_testcancel(void)
126 {
127 pthread_descr self = thread_self();
128 if (THREAD_GETMEM(self, p_canceled)
129 && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)
130 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
131 }
132
_pthread_cleanup_push(struct _pthread_cleanup_buffer * buffer,void (* routine)(void *),void * arg)133 void _pthread_cleanup_push(struct _pthread_cleanup_buffer * buffer,
134 void (*routine)(void *), void * arg)
135 {
136 pthread_descr self = thread_self();
137 buffer->__routine = routine;
138 buffer->__arg = arg;
139 buffer->__prev = THREAD_GETMEM(self, p_cleanup);
140 if (buffer->__prev != NULL && FRAME_LEFT (buffer, buffer->__prev))
141 buffer->__prev = NULL;
142 THREAD_SETMEM(self, p_cleanup, buffer);
143 }
144
_pthread_cleanup_pop(struct _pthread_cleanup_buffer * buffer,int execute)145 void _pthread_cleanup_pop(struct _pthread_cleanup_buffer * buffer,
146 int execute)
147 {
148 pthread_descr self = thread_self();
149 if (execute) buffer->__routine(buffer->__arg);
150 THREAD_SETMEM(self, p_cleanup, buffer->__prev);
151 }
152
_pthread_cleanup_push_defer(struct _pthread_cleanup_buffer * buffer,void (* routine)(void *),void * arg)153 void _pthread_cleanup_push_defer(struct _pthread_cleanup_buffer * buffer,
154 void (*routine)(void *), void * arg)
155 {
156 pthread_descr self = thread_self();
157 buffer->__routine = routine;
158 buffer->__arg = arg;
159 buffer->__canceltype = THREAD_GETMEM(self, p_canceltype);
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_canceltype, PTHREAD_CANCEL_DEFERRED);
164 THREAD_SETMEM(self, p_cleanup, buffer);
165 }
strong_alias(_pthread_cleanup_push_defer,__pthread_cleanup_push_defer)166 strong_alias(_pthread_cleanup_push_defer,__pthread_cleanup_push_defer)
167
168 void _pthread_cleanup_pop_restore(struct _pthread_cleanup_buffer * buffer,
169 int execute)
170 {
171 pthread_descr self = thread_self();
172 if (execute) buffer->__routine(buffer->__arg);
173 THREAD_SETMEM(self, p_cleanup, buffer->__prev);
174 THREAD_SETMEM(self, p_canceltype, buffer->__canceltype);
175 if (THREAD_GETMEM(self, p_canceled) &&
176 THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
177 THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
178 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
179 }
strong_alias(_pthread_cleanup_pop_restore,__pthread_cleanup_pop_restore)180 strong_alias(_pthread_cleanup_pop_restore,__pthread_cleanup_pop_restore)
181
182
183 void __pthread_perform_cleanup(char *currentframe)
184 {
185 pthread_descr self = thread_self();
186 struct _pthread_cleanup_buffer * c;
187
188 for (c = THREAD_GETMEM(self, p_cleanup); c != NULL; c = c->__prev)
189 {
190 #ifdef _STACK_GROWS_DOWN
191 if ((char *) c <= currentframe)
192 break;
193 #elif defined _STACK_GROWS_UP
194 if ((char *) c >= currentframe)
195 break;
196 #else
197 # error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP"
198 #endif
199 c->__routine(c->__arg);
200 }
201 }
202
203 #ifndef __PIC__
204 /* We need a hook to force the cancellation wrappers to be linked in when
205 static libpthread is used. */
206 extern const char __pthread_provide_wrappers;
207 static const char *const __pthread_require_wrappers =
208 &__pthread_provide_wrappers;
209 #endif
210